Différence entre le casting et l’utilisation de la méthode Convert.To ()

J’ai une fonction qui double ssortingng valeurs de ssortingng .

 ssortingng variable = "5.00"; double varDouble = (double)variable; 

Un changement de code a été archivé et le projet généré avec l’erreur: System.InvalidCastException: Specified cast is not valid.

Cependant, après avoir fait ce qui suit …

 ssortingng variable = "5.00"; double varDouble = Convert.ToDouble(variable); 

… le projet se construit sans erreur.

Quelle est la différence entre lancer et utiliser la méthode Convert.To() ? Pourquoi le casting lance-t-il une Exception et l’utilisation de Convert.To() ne le fait pas?

Même si vous les voyez comme équivalentes, leur objective est complètement différent. Essayons d’abord de définir ce qu’est un casting:

Le casting est l’action consistant à transformer une entité d’un type de données en une autre.

C’est un peu générique et il est en quelque sorte équivalent à une conversion car la dissortingbution a souvent la même syntaxe qu’une conversion. La question devrait donc se poser lorsqu’un cast (implicite ou explicite) est autorisé par le langage et quand utiliser un (plus) conversion explicite?

Laissez-moi d’abord tracer une ligne simple entre eux. Formellement (même si elle est équivalente pour la syntaxe du langage), une dissortingbution changera de type tandis qu’une conversion modifiera / peut changer de valeur (éventuellement avec un type). De plus, une dissortingbution est réversible alors que la conversion peut ne pas l’être.

Ce sujet est assez vaste, alors essayons de réduire un peu le problème en excluant les opérateurs de dissortingbution personnalisés du jeu.

Moulages implicites

En C #, une dissortingbution est implicite lorsque vous ne perdez aucune information (veuillez noter que cette vérification est effectuée avec des 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 moulages sont implicites car pendant la conversion, vous ne perdrez aucune information (vous élargissez simplement le type). Inversement, la conversion implicite n’est pas autorisée car, indépendamment de leurs valeurs réelles (car elles ne peuvent être vérifiées qu’au moment de l’exécution), vous risquez de perdre certaines informations pendant la conversion. Par exemple, ce code ne comstackra pas car un double peut contenir (et en fait il le fait) une valeur non représentable avec un float :

 double bigReal = Double.MaxValue; float tinyReal = bigReal; 

Objets

Dans le cas d’un object (un pointeur sur), la dissortingbution est toujours implicite lorsque le compilateur peut être sûr que le type de source est une classe dérivée (ou implémente) le type de la classe cible, par exemple:

 ssortingng text = "123"; IFormattable formattable = text; NotSupportedException derivedException = new NotSupportedException(); Exception baseException = derivedException; 

Dans ce cas, le compilateur sait que la ssortingng implémente IFormattable et que NotSupportedException est (dérive de) Exception donc la dissortingbution est implicite. Aucune information n’est perdue car les objects ne changent pas de type (ceci est différent avec les types struct et primitive car avec un casting, vous créez un nouvel object d’un autre type ), quels changements est votre vue sur eux.

Moulages explicites

Un cast est explicite lorsque la conversion n’est pas effectuée implicitement par le compilateur et que vous devez alors utiliser l’opérateur de conversion. Cela signifie généralement que:

  • Vous risquez de perdre des informations ou des données, vous devez donc en être conscient.
  • La conversion peut échouer (car vous ne pouvez pas convertir un type à un autre). Encore une fois, vous devez savoir ce que vous faites.

Types primitifs

Une conversion explicite est requirejse 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 plage float , vous perdrez des informations (dans ce cas précis) afin que la conversion soit explicite. Maintenant, essayez ceci:

 float max = (float)Double.MaxValue; 

Cette conversion échouera donc, encore une fois, elle doit être explicite afin 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 calculs d’exécution ou d’E / S). Retour à votre exemple:

 ssortingng text = "123"; double value = (double)text; 

Cela ne comstackra pas car le compilateur ne peut pas convertir le texte en nombres. Le texte peut contenir des caractères, pas seulement des nombres et c’est trop, en C #, même pour une dissortingbution explicite (mais cela peut être autorisé dans une autre langue).

Objets

La conversion de pointeurs (en objects) peut échouer si les types ne sont pas liés, par exemple ce code ne comstackra pas (car le compilateur sait qu’il n’y a pas de conversion possible):

 ssortingng text = (ssortingng)AppDomain.Current; Exception exception = (Exception)"abc"; 

Ce code comstackra mais il peut échouer au moment de l’exécution (cela dépend du type effectif des objects InvalidCastException ) avec une InvalidCastException :

 object obj = GetNextObjectFromInput(); ssortingng text = (ssortingng)obj; obj = GetNextObjectFromInput(); Exception exception = (Exception)obj; 

Conversions

Donc, enfin, si les conversions sont converties, alors pourquoi faut-il des classes comme Convert ? Ignorer les différences subtiles qui proviennent de l’implémentation de Convert et des implémentations IConvertible car en C # avec une dissortingbution, vous dites au compilateur:

croyez-moi, ce type est ce type même si vous ne pouvez pas le savoir maintenant, laissez-moi le faire et vous verrez.

-ou-

ne vous inquiétez pas, je m’en fous, quelque chose sera perdu dans cette conversion.

Pour toute autre chose, une opération plus explicite est nécessaire (pensez aux implications des conversions faciles , c’est pourquoi C ++ a introduit une syntaxe longue, explicite et détaillée pour eux). Cela peut impliquer une opération complexe (pour ssortingng -> double conversion, une parsing sera nécessaire). La conversion en ssortingng , par exemple, est toujours possible (via la méthode ToSsortingng() ), mais cela peut vouloir dire quelque chose de différent de ce que vous attendiez, donc plus explicite qu’un cast ( plus vous écrivez, plus vous pensez à ce que vous faites) ).

Cette conversion peut être effectuée à l’intérieur de l’object (à l’aide d’instructions IL connues), en utilisant des opérateurs de conversion personnalisés (définis dans la classe pour la conversion) ou des mécanismes plus complexes ( TypeConverter ou méthodes de classe, par exemple). Vous n’êtes pas au courant de ce qui va arriver mais vous savez que cela peut échouer (c’est pourquoi IMO lorsqu’une conversion plus contrôlée est possible, vous devriez l’utiliser). Dans votre cas, la conversion parsingra simplement la ssortingng pour produire un double :

 double value = Double.Parse(aSsortingngVariable); 

Bien sûr, cela peut échouer, donc si vous le faites, vous devriez toujours intercepter l’exception qu’il peut lancer ( FormatException ). C’est hors sujet ici mais si un TryParse est disponible alors vous devriez l’utiliser (parce que sémantiquement vous dites que ce n’est peut-être pas un nombre et qu’il est encore plus rapide … à échouer).

Les conversions dans .NET peuvent provenir de nombreux endroits, de TypeConverter , de TypeConverter implicites / explicites avec des opérateurs de conversion définis par l’utilisateur, d’implémentation de méthodes IConvertible et d’parsing (est-ce que j’ai oublié quelque chose?). Consultez 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 un casting pour convertir un type à un autre. C’est une méthode à l’intérieur d’une classe (celle qui sera lancée) qui dit “hé, si elle veut convertir ce type à ce type, alors je peux le faire”. Par exemple:

 float? maybe = 10; // Equals to Nullable maybe = 10; float sure1 = (float)maybe; // With cast float sure2 = maybe.Value; // Without cast 

Dans ce cas, il est explicite car il peut échouer mais ceci est laissé à l’implémentation (même s’il existe des instructions à ce sujet). Imaginez que vous écrivez une classe de chaîne personnalisée comme ceci:

 EasySsortingng text = "123"; // Implicit from ssortingng double value = (ssortingng)text; // Explicit to double 

Dans votre implémentation, vous pouvez décider de “simplifier la vie du programmeur” et exposer cette conversion via une dissortingbution (rappelez-vous qu’il s’agit simplement d’un raccourci pour écrire moins). Certains langages peuvent même permettre cela:

 double value = "123"; 

Permettre la conversion implicite à n’importe quel type (la vérification sera effectuée au moment de l’exécution). Avec les 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 une dissortingbution explicite:

  • Conversions entre types de base.
  • Conversions d’un object à un autre type (cela peut également inclure le déballage).
  • Conversions d’une classe dérivée en une classe de base (ou une interface implémentée).
  • Conversions d’un type à un autre via des opérateurs de conversion personnalisés.

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 conversion explicite.

Voyons maintenant quand vous pouvez utiliser Convert :

  • Conversions de n’importe quel type de base vers un autre type de base (avec certaines limitations, voir MSDN ).
  • Conversions de tout type implémentant IConvertible vers tout autre type (pris en charge).
  • Conversions de / vers un tableau d’ byte vers / depuis une chaîne.

Conclusions

IMO Convert doit être utilisé chaque fois que vous savez qu’une conversion peut échouer (en raison du format, de la scope ou du fait qu’elle ne soit pas prise en charge), même si la conversion peut être effectuée avec une conversion (sauf autre). Cela indique clairement qui va lire votre code, quelle est votre intention et que cela risque d’échouer (simplifiant le débogage).

Pour tout le rest, vous devez utiliser une dissortingbution, sans choix, mais si une autre méthode est disponible, je vous suggère de l’utiliser. Dans votre exemple, une conversion de ssortingng en double est quelque chose qui échouera très souvent (en particulier si le texte provient de l’utilisateur), vous devez donc le rendre le plus explicite possible (en outre, vous avez plus de contrôle), par exemple en utilisant une méthode TryParse .

Edit: quelle est la différence entre eux?

Selon la question mise à jour et en gardant ce que j’ai déjà écrit (à propos du moment où vous pouvez utiliser / Convert ), le dernier point à clarifier est la différence entre eux (de plus, Convert utilise des interfaces IConvertible et IFormattable ). il peut effectuer des opérations non autorisées avec les casts).

Réponse courte est oui, ils se comportent différemment . Je vois la classe Convert comme une classe de méthodes d’assistance si souvent qu’elle apporte 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? Cast tronque (c’est ce à quoi nous nous attendons tous) mais Convert effectue un arrondi au nombre entier le plus proche (et cela peut ne pas être prévu si vous ne le connaissez pas). 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 MSDN case by Cas!

Casting est une façon de dire au compilateur: “Je sais que vous pensez que cette variable est une barre, mais il se trouve que je connais plus que vous; l’object est en fait un Foo, alors laissez-moi le traiter comme un Foo de maintenant sur ” Puis, à l’exécution, si l’object réel s’est avéré être réellement un Foo, alors votre code fonctionne, s’il s’avère que l’object n’était pas du tout un Foo, alors vous obtenez une exception. (Plus précisément, une System.InvalidCastException .)

La conversion par contre est une façon de dire “Si vous me donnez un object de type Bar je peux créer un nouvel object Foo qui représente ce qui se trouve dans cet object Bar. Je ne changerai pas l’object original, il a gagné” Pour traiter l’object original différemment, il créera quelque chose de nouveau qui sera basé sur une autre Convert.ToDouble ce qui concerne la façon dont cela se fera, cela pourrait être n’importe quoi. Dans le cas de Convert.ToDouble il finira par appeler Double.Parse possède toutes sortes de logiques complexes 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 les chaînes de manière différente (peut-être pour prendre en charge des conventions d’affichage différentes, telles que des chiffres romains ou autres). Une conversion peut faire n’importe quoi, mais l’idée est que vous ne demandez pas vraiment au compilateur de faire quelque chose pour vous, vous écrivez le code pour déterminer comment créer le nouvel object car le compilateur, sans votre aide, n’a pas façon de savoir h ow pour mapper (par exemple) une ssortingng à un double .

Alors, quand 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 object A vraiment, en fait, sous le capot, c’est un B, alors nous lançons. Si ce n’est pas vraiment un B, alors nous devons le convertir et définir comment le programme est supposé obtenir un B à partir d’un A.

La méthode Convert.Double appelle Double.Parse(ssortingng) méthode Double.Parse(ssortingng) interne.

Ni le type Ssortingng ni le type Double définissent une conversion explicite / implicite entre les deux types, de sorte que la diffusion échouera toujours.

La méthode Double.Parse examinera chaque caractère de la ssortingng et générera une valeur numérique basée sur les valeurs des caractères de la ssortingng . Si l’un des caractères n’est pas valide, la méthode Parse échoue (entraînant l’ Convert.Double méthode Convert.Double ).

Dans votre exemple, vous essayez de convertir une chaîne en double (type non intégral).

Une conversion explicite est nécessaire pour que cela fonctionne.

Et je dois signaler que vous auriez pu utiliser Convert.ToDouble au lieu de Convert.ToInt64 car vous pouvez perdre les parties fractionnaires de la valeur double lorsque vous convertissez en un int.

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 le casting vs la conversion.

Votre dissortingbution (une dissortingbution explicite) ne répond pas aux exigences pour une dissortingbution explicite. la valeur que vous essayez de lancer avec l’opérateur de conversion est invalide (c’est-à-dire non intégrale).

Visitez cette page MSDN pour les règles de casting / conversions

Le casting 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 ssortingng. ssortingng s = (ssortingng)o; // This works only if o really contains a ssortingng or null. 

Vous pouvez convertir un double en ssortingng comme ceci

 double d = 5; ssortingng s = d.ToSsortingng(); // -> "5" // Or by specifying a format ssortingng formatted = d.ToSsortingng("N2"); // -> "5.00" 

Vous pouvez convertir une ssortingng en double de plusieurs manières (ici deux seulement):

 ssortingng s = "5"; double d = Double.Parse(s); // Throws an exception if s does not contain a valid number 

Ou le moyen sûr

 ssortingng s = "5"; double d; if (Double.TryParse(s, out d)) { Console.WriteLine("OK. Result = {0}", d); } else { Console.WriteLine("oops!"); } 

De MSDN :

Conversions explicites (conversions): les conversions explicites nécessitent un opérateur de dissortingbution. Le casting est requirejs lorsque des informations peuvent être perdues lors de la conversion ou lorsque la conversion peut échouer pour d’autres raisons. Des exemples typiques incluent la conversion numérique en un type moins précis ou avec une plage plus petite, et la conversion d’une instance de classe de base en une classe dérivée.

Prenons l’exemple suivant:

 double a = 2548.3; int b; b = (int)a; //2548 --> information (.3) lost in the conversion 

Et aussi:

Un cast est un moyen d’informer explicitement le compilateur que vous avez l’intention d’effectuer la conversion et que vous êtes conscient que des pertes de données peuvent se produire.

Vous pouvez utiliser la classe System.Convert lorsque vous souhaitez convertir entre des types non compatibles . La principale différence entre le casting et la conversion est la compilation et l’ exécution . Les exceptions de conversion de type sont InvalidCastException au InvalidCastException de l’ InvalidCastException à-d. InvalidCastException conversion de type qui échoue au moment de l’exécution provoquera une InvalidCastException .


Conclusion: En casting, vous dites au compilateur que a est vraiment du type b et si le projet est construit sans erreur comme dans cet exemple:

 double s = 2; int a = (int) s; 

Mais lors de la conversion que vous dites au compilateur, il existe un moyen de créer un nouvel object à partir du type b , faites-le et le projet se construit sans erreur, mais comme je l’ai dit, le type échoue à l’exécution. une InvalidCastException à lancer .

Par exemple, le code ci-dessous n’est jamais compilé car le compilateur détecte qu’il ne peut pas exprimer l’expression de type DateTime pour taper int :

 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 à l’exécution, vous obtiendrez InvalidCastException qui indique:

Dissortingbution incorrecte de ‘DateTime’ à ‘Int32’.

 ssortingng 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 castings explicites pour les types numériques: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Comme vous pouvez le voir, même chaque type numérique ne peut pas être converti en un autre type numérique

Plus d’infos sur le casting ici

Et en quoi est-ce différent de Convert.ToDouble ()?

Lorsque vous convertissez un type, la structure de données n’est pas modifiée. Eh bien, en cas de conversion de valeurs numériques, vous pouvez perdre quelques bits ou obtenir quelques bits supplémentaires. Mais vous travaillez toujours avec un numéro. Vous modifiez simplement une quantité de mémoire prise par ce nombre. C’est assez sûr pour que le compilateur fasse tout le nécessaire.

Mais lorsque vous essayez de convertir une chaîne en nombre, vous ne pouvez pas le faire car cela ne suffit pas pour modifier la quantité de mémoire prise par la variable. Par exemple, 5.00 en tant que chaîne est une suite de “nombres”: 53 (5) 46 (.) 48 (0) 48 (0) – c’est-à-dire pour ASCII, mais ssortingng contiendra quelque chose de similaire. Si le compilateur ne prend que 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, calcule le chiffre qu’il représente et crée un double numéro pour vous, si chaîne représente un nombre. En gros, les langages comme PHP appellent 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 comme:

 double d = (double)"zzzz" 

Lancer une chaîne sur un double comme cela n’est pas autorisé C # c’est pourquoi vous obtenez une exception, vous devez avoir la chaîne convertie ( doc 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 (sauf les valeurs nulles). Un Convert exécutera une méthode qui vérifiera la chaîne pour voir si elle peut être convertie en une valeur numérique. Si c’est possible, la valeur retournera. Si ce n’est pas le cas, une exception sera lancée.

Pour le convertir, vous avez plusieurs options. Vous avez utilisé la méthode Convert dans votre question, il y a Parse qui est en grande partie similaire à Convert , mais vous devriez également regarder TryParse qui vous permettrait de faire:

 ssortingng 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 de Convert ou d’ Parse une chaîne non numérique.

double varDouble = (double)variable suppose que la variable est déjà un double. Si variable 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 parsingr ou extraire un double de la variable il le fera.

J’utilise ensuite Double.Parse ou Double.TryParse car il indique plus clairement ce qui est supposé se produire. Vous commencez avec une chaîne et vous vous attendez à ce qu’il soit convertible en double. En cas de doute, utilisez TryParse .

Si variable est un argument de méthode, remplacez le type par double. Rendre l’appelant responsable de fournir le type correct. De cette façon, le compilateur fait le travail pour vous.