J'ai une fonction qui jette un double
sur les string
valeurs.
string variable = "5.00";
double varDouble = (double)variable;
Une modification de code a été archivée et le projet se construit avec l'erreur: System.InvalidCastException: Specified cast is not valid.
Cependant, après avoir fait ce qui suit ...
string variable = "5.00";
double varDouble = Convert.ToDouble(variable);
... le projet se construit sans aucune erreur.
Quelle est la différence entre le moulage et l'utilisation de la Convert.To()
méthode? Pourquoi le casting lance-t-il un Exception
et l'utilisation du Convert.To()
ne le fait pas?
Réponses:
Même si vous pouvez les voir d'une manière ou d'une autre comme équivalents, leur objectif est complètement différent. Essayons d'abord de définir ce qu'est un casting:
C'est un peu générique et c'est en quelque sorte équivalent à une conversion car un cast a souvent la même syntaxe d'une conversion, donc la question devrait être quand un cast (implicite ou explicite) est autorisé par le langage et quand faut-il utiliser un ( plus) conversion explicite?
Permettez-moi d'abord de tracer une ligne simple entre eux. Formellement (même si équivalent pour la syntaxe du langage) un casting va changer le type tandis qu'une conversion / peut changer la valeur (éventuellement en même temps avec le type). De plus, un casting est réversible alors qu'une conversion peut ne pas l'être.
Ce sujet est assez vaste, alors essayons de le réduire un peu en excluant les opérateurs de diffusion personnalisés du jeu.
Casts implicites
En C #, un cast est implicite lorsque vous ne perdez aucune information (veuillez noter que cette vérification est effectuée avec les types et non avec leurs valeurs réelles ).
Types primitifs
Par exemple:
int tinyInteger = 10; long bigInteger = tinyInteger; float tinyReal = 10.0f; double bigReal = tinyReal;
Ces transtypages sont implicites car lors de la conversion, vous ne perdrez aucune information (vous élargissez simplement le type). À l'inverse, la conversion implicite n'est pas autorisée car, quelles que soient leurs valeurs réelles (car elles ne peuvent être vérifiées qu'au moment de l'exécution), pendant la conversion, vous risquez de perdre certaines informations. Par exemple, ce code ne se compilera pas car a
double
peut contenir (et en fait il le fait) une valeur non représentable avec afloat
:// won't compile! double bigReal = Double.MaxValue; float tinyReal = bigReal;
Objets
Dans le cas d'un objet (un pointeur vers), le cast est toujours implicite lorsque le compilateur peut être sûr que le type source est une classe dérivée (ou il implémente) le type de la classe cible, par exemple:
string text = "123"; IFormattable formattable = text; NotSupportedException derivedException = new NotSupportedException(); Exception baseException = derivedException;
Dans ce cas, le compilateur sait que
string
implémenteIFormattable
et quiNotSupportedException
est (dérive de)Exception
donc le cast est implicite. Aucune information n'est perdue car les objets ne changent pas de type (c'est différent avecstruct
les types s et primitifs car avec une distribution vous créez un nouvel objet d'un autre type ), ce qui change, c'est votre vision d'eux.Casts explicites
Un cast est explicite lorsque la conversion n'est pas effectuée implicitement par le compilateur et que vous devez ensuite utiliser l'opérateur de cast. Cela signifie généralement que:
Types primitifs
Un cast explicite est requis pour les types primitifs lorsque, lors de la conversion, vous risquez de perdre certaines données, par exemple:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456); float coarse = (float)precise; float epsilon = (float)Double.Epsilon;
Dans les deux exemples, même si les valeurs se situent dans la
float
plage, vous perdrez des informations (dans ce cas, la précision), donc la conversion doit être explicite. Maintenant, essayez ceci:float max = (float)Double.MaxValue;
Cette conversion échouera donc, encore une fois, elle doit être explicite pour que vous en soyez conscient et que vous puissiez faire une vérification (dans l'exemple, la valeur est constante mais elle peut provenir de certains calculs d'exécution ou d'E / S). Revenons à votre exemple:
// won't compile! string text = "123"; double value = (double)text;
Cela ne compilera pas car le compilateur ne peut pas convertir le texte en nombres. Le texte peut contenir n'importe quel caractère, pas seulement des nombres et c'est trop, en C #, même pour un cast explicite (mais cela peut être autorisé dans une autre langue).
Objets
Les conversions de pointeurs (en objets) peuvent échouer si les types ne sont pas liés, par exemple ce code ne se compilera pas (car le compilateur sait qu'il n'y a pas de conversion possible):
// won't compile! string text = (string)AppDomain.Current; Exception exception = (Exception)"abc";
Ce code se compilera mais il peut échouer au moment de l'exécution (cela dépend du type effectif des objets castés) avec un
InvalidCastException
:object obj = GetNextObjectFromInput(); string text = (string)obj; obj = GetNextObjectFromInput(); Exception exception = (Exception)obj;
Les conversions
Donc, enfin, si les casts sont des conversions, pourquoi avons-nous besoin de classes comme
Convert
? En ignorant les différences subtiles qui proviennent de l'Convert
implémentation et desIConvertible
implémentations en fait, car en C # avec un cast, vous dites au compilateur:-ou-
Pour tout le reste, une opération plus explicite est nécessaire (pensez aux implications des transtypages faciles , c'est pourquoi C ++ a introduit une syntaxe longue, verbeuse et explicite pour eux). Cela peut impliquer une opération complexe (pour
string
-> ladouble
conversion, une analyse sera nécessaire). Une conversion enstring
, par exemple, est toujours possible (via laToString()
méthode) mais cela peut signifier quelque chose de différent de ce que vous attendez donc cela doit être plus explicite qu'un casting ( plus vous écrivez, plus vous pensez à ce que vous faites ).Cette conversion peut être effectuée à l'intérieur de l'objet (en utilisant des instructions IL connues pour cela), en utilisant des opérateurs de conversion personnalisés (définis dans la classe à convertir) ou des mécanismes plus complexes (
TypeConverter
s ou méthodes de classe, par exemple). Vous ne savez pas ce qui va se passer pour faire cela, mais vous savez que cela peut échouer (c'est pourquoi l'OMI, lorsqu'une conversion plus contrôlée est possible, vous devriez l'utiliser). Dans votre cas, la conversion analysera simplement lestring
pour produire undouble
:double value = Double.Parse(aStringVariable);
Bien sûr, cela peut échouer, donc si vous le faites, vous devriez toujours attraper l'exception qu'il peut lancer (
FormatException
). C'est hors sujet ici, mais quand aTryParse
est disponible, vous devriez l'utiliser (car sémantiquement vous dites que ce n'est peut-être pas un nombre et que c'est encore plus rapide ... d'échouer).Les conversions dans .NET peuvent provenir de nombreux endroits, des
TypeConverter
conversions implicites / explicites avec des opérateurs de conversion définis par l'utilisateur, l'implémentationIConvertible
et des méthodes d'analyse (ai-je oublié quelque chose?). Jetez un œil sur MSDN pour plus de détails à leur sujet.Pour terminer cette longue réponse, quelques mots sur les opérateurs de conversion définis par l'utilisateur. C'est juste du sucre de laisser le programmeur utiliser une distribution pour convertir un type en un autre. C'est une méthode à l'intérieur d'une classe (celle qui sera castée) qui dit "hé, si il / elle veut convertir ce type en ce type alors je peux le faire". Par exemple:
float? maybe = 10; // Equals to Nullable<float> maybe = 10; float sure1 = (float)maybe; // With cast float sure2 = maybe.Value; // Without cast
Dans ce cas, c'est explicite car il peut échouer mais cela est laissé à l'implémentation (même s'il existe des directives à ce sujet). Imaginez que vous écrivez une classe de chaîne personnalisée comme celle-ci:
EasyString text = "123"; // Implicit from string double value = (string)text; // Explicit to double
Dans votre implémentation, vous pouvez décider de "faciliter la vie du programmeur" et d'exposer cette conversion via un cast (rappelez-vous que c'est juste un raccourci pour écrire moins). Certaines langues peuvent même permettre ceci:
double value = "123";
Autoriser la conversion implicite vers n'importe quel type (la vérification sera effectuée au moment de l'exécution). Avec des options appropriées, cela peut être fait, par exemple, dans VB.NET. C'est juste une philosophie différente.
Que puis-je faire avec eux?
Donc, la dernière question est de savoir quand utiliser l'un ou l'autre. Voyons quand vous pouvez utiliser un cast explicite:
object
vers tout autre type (cela peut également inclure le déballage).Seule la première conversion peut être effectuée avec
Convert
donc pour les autres vous n'avez pas le choix et vous devez utiliser une distribution explicite.Voyons maintenant quand vous pouvez utiliser
Convert
:IConvertible
vers tout autre type (pris en charge).byte
tableau vers / depuis une chaîne.Conclusions
IMO
Convert
doit être utilisé chaque fois que vous savez qu'une conversion peut échouer (à cause du format, à cause de la plage ou parce qu'elle peut ne pas être prise en charge), même si la même conversion peut être effectuée avec une conversion (à moins que quelque chose d'autre ne soit disponible). Il indique clairement à qui lira votre code quelle est votre intention et que cela peut échouer (simplification du débogage).Pour tout le reste, vous devez utiliser un casting, pas de choix, mais si une autre meilleure méthode est disponible, je vous suggère de l'utiliser. Dans votre exemple, une conversion de
string
versdouble
est quelque chose qui (surtout si le texte provient de l'utilisateur) échouera très souvent, vous devez donc la rendre aussi explicite que possible (de plus, vous obtenez plus de contrôle sur elle), par exemple en utilisant uneTryParse
méthode.Edit: quelle est la différence entre eux?
Selon la question mise à jour et en gardant ce que j'ai écrit auparavant (à propos du moment où vous pouvez utiliser un casting par rapport au moment où vous pouvez / devez utiliser
Convert
), le dernier point à clarifier est s'il y a une différence entre eux (de plusConvert
utiliseIConvertible
etIFormattable
interfaces afin qu'il puisse effectuer des opérations non autorisé avec les moulages).La réponse courte est oui, ils se comportent différemment . Je vois la
Convert
classe comme une classe de méthodes d'assistance si souvent qu'elle offre des avantages ou des comportements légèrement différents. Par exemple:double real = 1.6; int castedInteger = (int)real; // 1 int convertedInteger = Convert.ToInt32(real); // 2
Assez différent, non? Le cast tronque (c'est ce que nous attendons tous) mais
Convert
effectue un arrondi à l'entier le plus proche (et cela peut ne pas être attendu si vous n'en êtes pas conscient). Chaque méthode de conversion introduit des différences donc une règle générale ne peut pas être appliquée et elles doivent être vues au cas par cas ... 19 types de base à convertir en tout autre type ... la liste peut être assez longue, mieux vaut consulter le cas MSDN en Cas!la source
Difference between casting and using the Convert.To() method
. Sinon, réponse très complète. (J'espère que ma question est rouverte ...)double
laquelle les valeurs qui ne représentent pas des nombres entiers devraient être «convertibles» enint
. Un casting semble le paradigme approprié dans les cas où l' on est par exemple la récupération desInt32
valeurs d'undouble[]
qui détient un mélange de nombres réels et desInt32
valeurs qui ont été converties endouble
[une tentative de convertir une valeur non représentable précisément enint32
indiquerait une condition inattendue et devrait déclencher une exception], mais je pense que quand on veut une conversion avec perte, on devrait être précis sur la forme que l'on veut.object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
float
->int
) mais une coercition . Un casting pourrait être par exempleDerivedClass
->BaseClass
. C'est déroutant parce qu'en C #, nous utilisons le même mot (et opérateur) pour les deux, mais ce sont en fait des choses distinctes. Une définition formelle pour les distinguer est légèrement plus compliquée que ce que j'ai écrit.La diffusion est une façon de dire au compilateur: «Je sais que vous pensez que cette variable est une barre, mais j'en sais plus que vous; l'objet est en fait un Foo, alors laissez-moi le traiter comme s'il s'agissait d'un Foo de maintenant. " Ensuite, au moment de l'exécution, si l'objet réel s'est avéré être vraiment un Foo, votre code fonctionne, s'il s'avère que l'objet n'était pas du tout un Foo, vous obtenez une exception. (Plus précisément un
System.InvalidCastException
.)La conversion, par contre, est une façon de dire: "Si vous me donnez un objet de type Bar, je peux créer un tout nouvel objet Foo qui représente ce qu'il y a dans cet objet Bar. Je ne changerai pas l'objet d'origine, il gagnera" t traiter l'objet d'origine différemment, il créera quelque chose de nouveau qui est juste basé sur une autre valeur . Quant à la façon dont il le fera, il pourrait être n'importe quoi. Dans ce cas,
Convert.ToDouble
il finira par appelerDouble.Parse
qui a toutes sortes de logique complexe pour déterminer quels types de chaînes représentent quelles valeurs numériques. Vous pouvez écrire votre propre méthode de conversion qui mappe différemment les chaînes aux doubles (peut-être pour prendre en charge une convention entièrement différente pour l'affichage des nombres, comme les chiffres romains ou autre). Une conversion peut faire n'importe quoi, mais l'idée est que vous ne demandez pas vraiment au compilateur de faire quoi que ce soit pour vous; c'est vous qui écrivez le code pour déterminer comment créer le nouvel objet car le compilateur, sans votre aide, n'a aucun moyen de savoir comment mapper (à titre d'exemple) astring
à adouble
.Alors, quand vous convertissez-vous et quand lancez-vous? Dans les deux cas, nous avons une variable d'un type, disons A, et nous voulons avoir une variable de type B. Si notre objet A vraiment, en fait, sous le capot, est un B, alors nous castons. Si ce n'est pas vraiment un B, nous devons le convertir et définir comment le programme est censé obtenir un B à partir d'un A.
la source
foreach
). En dehors de ces exceptions, les moulages sont par définition explicites.De
MSDN
:Prenons l'exemple suivant:
double a = 2548.3; int b; b = (int)a; //2548 --> information (.3) lost in the conversion
Et aussi:
Vous pouvez utiliser la
System.Convert
classe lorsque vous souhaitez effectuer une conversion entre des types non compatibles . La principale différence entre la diffusion et la conversion est la compilation et l' exécution . Les exceptions de conversion de type sont apparues au moment de l' exécution , c'est -à- dire qu'un cast de type qui échoue au moment de l'exécution provoquera laInvalidCastException
levée d'un.Conclusion: lors du casting, vous indiquez au compilateur qui
a
est vraiment de typeb
et si tel est le cas, le projet se construit sans aucune erreur comme cet exemple:double s = 2; int a = (int) s;
Mais dans la conversion vous dites au compilateur , il est un moyen de créer un nouvel objet à partir
a
du typeb
, s'il vous plaît le faire et projet construit sans erreur , mais comme je le disais si le type cast échoue à l' exécution, il fera unInvalidCastException
à être jeté .Par exemple, le code ci-dessous n'est jamais compilé car le compilateur détecte que ne peut pas convertir une expression de type
DateTime
en typeint
:DateTime s = DateTime.Now; int a = (int)(s);
Mais celui-ci est compilé avec succès:
DateTime s = DateTime.Now; int a = Convert.ToInt32(s);
Mais au moment de l'exécution, vous obtiendrez
InvalidCastException
ce qui dit:la source
La
Convert.Double
méthode appelle en fait la méthode en interneDouble.Parse(string)
.Ni le
String
type ni leDouble
type ne définissent une conversion explicite / implicite entre les deux types, donc la conversion échouera toujours.La
Double.Parse
méthode examinera chaque caractère dustring
et créera une valeur numérique basée sur les valeurs des caractères dustring
. Si l'un des caractères n'est pas valide, laParse
méthode échoue (ce qui entraîne également l'Convert.Double
échec de la méthode).la source
Convert.ToDouble()
regard au-delà des octets et prendrait -il en compte les données?Dans votre exemple, vous essayez de convertir une chaîne en un double (type non intégral).
Une conversion explicite est nécessaire pour que cela fonctionne.
Et je dois souligner que vous auriez pu utiliser
Convert.ToDouble
au lieu deConvert.ToInt64
car vous pouvez perdre les parties fractionnaires de la valeur double lorsque vous convertissez en un entier.si votre variable a la valeur «5,25» varDouble aurait été 5,00 (perte de 0,25 à cause de la conversion en Int64)
Pour répondre à votre question sur la diffusion et la conversion.
Votre distribution (une distribution explicite) ne répond pas aux exigences d'une distribution explicite. la valeur que vous essayez de convertir avec l'opérateur de conversion n'est pas valide (c'est-à-dire non intégrale).
Visitez cette page MSDN pour les règles de diffusion / conversions
la source
La diffusion n'implique aucune conversion, c'est-à-dire que la représentation interne d'une valeur n'est pas modifiée. Exemple:
object o = "Hello"; // o is typed as object and contains a string. string s = (string)o; // This works only if o really contains a string or null.
Vous pouvez convertir un
double
àstring
comme celui - cidouble d = 5; string s = d.ToString(); // -> "5" // Or by specifying a format string formatted = d.ToString("N2"); // -> "5.00"
Vous pouvez convertir a
string
en adouble
de plusieurs manières (ici seulement deux d'entre elles):string s = "5"; double d = Double.Parse(s); // Throws an exception if s does not contain a valid number
Ou la manière sûre
string s = "5"; double d; if (Double.TryParse(s, out d)) { Console.WriteLine("OK. Result = {0}", d); } else { Console.WriteLine("oops!"); }
la source
Convert.ToDouble()
appels en interneDouble.Parse()
. Est-ce à mon avantage d'utiliserConvert.ToDouble()
plusDouble.Parse()
ou pas et pourquoi?Convert.ToDouble
a beaucoup de surcharges qui acceptent différents types d'entrée. La surcharge acceptant lesstring
retours0.0
si unenull
chaîne est passée. En dehors de cela, je ne vois aucun avantage à l'utiliser.Double.Parse()
a quelque chose à offrir que je devrais envisager?Double.Parse()
est plus direct queConvert.ToDouble()
. Si vous êtes sûr que votre chaîne contiendra un nombre valide, vous pouvez l'utiliser en toute sécurité, sinon je vous conseille de l'utiliserDouble.TryParse
.string variable = "5.00"; double varDouble = (double)variable;
La conversion ci-dessus n'est tout simplement pas autorisée par la langue. Voici une liste de casts explicites pour les types numériques: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Comme vous pouvez le voir, même tous les types numériques ne peuvent pas être convertis en un autre type numérique
Quelques informations supplémentaires sur la diffusion ici
Lorsque vous castez un type, la structure des données n'est pas modifiée. Eh bien, en cas de conversion de valeurs numériques, vous risquez de perdre quelques bits ou d'obtenir quelques 0 bits supplémentaires. Mais vous travaillez toujours avec un certain nombre. Vous modifiez simplement une quantité de mémoire prise par ce nombre. C'est suffisamment sûr pour que le compilateur fasse tout ce qui est nécessaire.
Mais lorsque vous essayez de convertir une chaîne en un nombre, vous ne pouvez pas le faire car il ne suffit pas de modifier la quantité de mémoire prise par la variable. Par exemple, 5,00comme une chaîne est une séquence de "nombres": 53 (5) 46 (.) 48 (0) 48 (0) - c'est pour ASCII, mais la chaîne contiendra quelque chose de similaire. Si le compilateur prend juste les N premiers octets (4 pour le double? Pas sûr) d'une chaîne - ce morceau contiendra un nombre double complètement différent. En même temps, Convert.ToDouble () exécute un algorithme spécial qui prend chaque symbole d'une chaîne, détermine le chiffre qu'il représente et crée un double nombre pour vous, si la chaîne représente un nombre. Des langages comme PHP appelleront, grosso modo, Convert.ToDouble pour vous en arrière-plan. Mais C #, comme un langage typé statiquement, ne le fera pas pour vous. Cela vous permet d'être sûr que toute opération est de type sécurisé et que vous n'obtiendrez pas quelque chose d'inattendu en faisant quelque chose comme:
double d = (double)"zzzz"
la source
Le cast d'une chaîne en un double comme celui-ci n'est pas autorisé C #, c'est pourquoi vous obtenez une exception, vous devez avoir la chaîne convertie ( document MSDN qui montre les chemins de conversion acceptables). C'est simplement parce qu'une chaîne ne va pas nécessairement contenir des données numériques, mais les différents types numériques le feront (sauf les valeurs nulles). A
Convert
exécutera une méthode qui vérifiera la chaîne pour voir si elle peut être transformée en valeur numérique. Si c'est possible, il renverra cette valeur. S'il ne peut pas, il lèvera une exception.Pour le convertir, vous avez plusieurs options. Vous avez utilisé la
Convert
méthode dans votre question, il y en aParse
qui est en grande partie similaire àConvert
, mais vous devriez également regarder TryParse qui vous permettrait de faire:string variable = "5.00"; double varDouble; if (Double.TryParse(variable, out varDouble)) { //Code that runs if the conversion succeeded. } else { //Code that runs if the conversion failed. }
Cela évite l'exception possible si vous essayez d'
Convert
utiliserParse
une chaîne non numérique.la source
TryParse
overConvert
carTryParse
vérifie si la conversion réussit?double varDouble = (double)variable
suppose quevariable
c'est déjà un double. Si cevariable
n'est pas un double (c'est une chaîne), cela échouera.double varDouble = Convert.ToDouble(variable)
fait comme il dit - il convertit. S'il peut analyser ou extraire un double,variable
il le fera.Je seconde en utilisant
Double.Parse
ouDouble.TryParse
parce qu'il indique plus clairement ce qui est censé se passer. Vous commencez par une chaîne et vous vous attendez à ce qu'elle soit convertible en double. En cas de doute, utilisezTryParse
.Si
variable
est un argument de méthode, changez le type en double. Rendre l'appelant responsable de fournir le type correct. De cette façon, le compilateur fait le travail pour vous.la source
La différence la plus importante est que si la conversion de type est utilisée et que la conversion échoue (disons que nous convertissons une très grande valeur flottante en int), aucune exception ne sera lancée et la valeur minimale qu'un int peut contenir sera affichée. Mais en cas d'utilisation de Convert , une exception sera levée pour de tels scénarios.
la source