120 votes

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

J'ai une fonction qui convertit un double en string valeurs.

string variable = "5.00"; 

double varDouble = (double)variable;

Un changement de code a été enregistré 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 moulage lance un Exception et en utilisant le Convert.To() ne le fait pas ?

160voto

Adriano Repetti Points 22087

Même si vous mai les voir en quelque sorte comme équivalents, ils ont un but complètement différent. Essayons d'abord de définir ce qu'est un casting :

Le Casting est l'action de changer une entité d'un type de données en un autre.

C'est un peu générique et c'est en quelque sorte l'équivalent d'une conversion parce qu'un casting a souvent la même syntaxe qu'une conversion, donc la question devrait être quand un cast (implicite ou explicite) est autorisé par le langage et quand devez-vous utiliser une conversion (plus) explicite ?

Laissez-moi d'abord dessiner une simple ligne entre eux. Formellement (même si c'est équivalent pour la syntaxe du langage), un cast changera le type alors qu'une conversion changera/peut changer la valeur (éventuellement ensemble avec le type). De plus, un moulage est réversible alors qu'une conversion peut ne pas l'être.

Ce sujet est assez vaste, essayons donc de le restreindre un peu en excluant du jeu les opérateurs de cast personnalisé.

Moulages 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 casts sont implicites car, lors de la conversion, vous ne perdez aucune information (vous élargissez simplement le type). Vice versa, les cast implicites ne sont pas autorisés parce que, indépendamment de leurs valeurs réelles (parce qu'ils ne peuvent être vérifiés qu'à l'exécution), pendant la conversion, vous pouvez perdre des informations. Par exemple, ce code ne compilera pas car un type double peut contenir (et c'est effectivement le cas) une valeur qui n'est pas représentable avec un float :

// 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 qu'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 connaît que string met en œuvre IFormattable et que NotSupportedException est (dérive de) Exception donc la distribution est implicite. Aucune information n'est perdue car les objets ne changent pas de type (ceci est différent avec struct et les types primitifs, car avec un cast vous créez un nouvel objet d'un autre type ), ce qui change est votre voir d'entre eux.

Transferts 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 cast. En général, cela signifie que :

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

Types primitifs

Un cast explicite est nécessaire pour les types primitifs lorsque, pendant la conversion, vous pouvez 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 à l'intérieur du float vous perdrez de l'information (dans ce cas, la précision). La conversion doit donc être explicite. Essayez maintenant 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 ou E/S en cours d'exécution). Revenons à votre exemple :

// won't compile!
string text = "123";
double value = (double)text;

Cela ne compilera pas car le compilateur ne peut pas convertir du texte en chiffres. Le texte peut contenir n'importe quel caractère, mais pas uniquement des chiffres et c'est trop, en C#, même pour un cast explicite (mais cela peut être autorisé dans un autre langage).

Objets

Les conversions de pointeurs (en objets) peuvent échouer si les types ne sont pas liés, par exemple ce code ne compilera pas (parce que 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 compilera mais il peut échouer à l'exécution (cela dépend du type effectif des objets casted) avec une erreur de type InvalidCastException :

object obj = GetNextObjectFromInput();
string text = (string)obj;

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

Conversions

Enfin, si les castings sont des conversions, pourquoi avons-nous besoin de classes telles que Convert ? En ignorant les différences subtiles qui proviennent Convert mise en œuvre et IConvertible car en C#, avec un cast, vous dites au compilateur :

Faites-moi confiance, 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 ne me soucie pas si quelque chose sera perdu dans cette conversion.

Pour toute autre chose, un plus une opération explicite est nécessaire (pensez aux implications de moulages faciles c'est pourquoi le C++ a introduit une syntaxe longue, verbeuse et explicite pour eux). Cela peut impliquer une opération complexe (pour string -> double une analyse syntaxique sera nécessaire). Une conversion en string par exemple, est toujours possible (via ToString() ) mais il peut signifier quelque chose de différent de ce que vous attendez, il doit donc ê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 les 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 ou des méthodes de classe, par exemple). Vous ne savez pas ce qui se passera pour faire cela, mais vous êtes conscient que cela peut échouer (c'est pourquoi l'OMI quand un plus contrôlé la conversion est possible, vous devriez l'utiliser). Dans votre cas, la conversion analysera simplement le fichier string pour produire un double :

double value = Double.Parse(aStringVariable);

Bien sûr, cela peut échouer, donc si vous le faites, vous devez toujours attraper l'exception qu'il peut lancer ( FormatException ). C'est hors sujet ici mais quand un TryParse est disponible, alors vous devez l'utiliser (parce que sémantiquement vous dites il se peut que ce ne soit pas un nombre et c'est encore plus rapide... d'échouer).

Les conversions dans .NET peuvent provenir de nombreux endroits, TypeConverter les conversions implicites/explicites avec des opérateurs de conversion définis par l'utilisateur, la mise en œuvre d'un système de gestion de l'information et de la communication. IConvertible et les méthodes d'analyse syntaxique (ai-je oublié quelque chose ?). Jetez un coup d'œ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. Il s'agit simplement sucre pour permettre au programmeur d'utiliser un cast pour convertir un type en un autre. Il s'agit d'une méthode à l'intérieur d'une classe (celle qui sera castée) qui dit "hey, 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 parce que cela 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 ceci :

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). Certains langages peuvent même le permettre :

double value = "123";

Permettre la conversion implicite vers n'importe quel type (la vérification sera faite au moment de l'exécution). Avec les options appropriées, cela peut être fait, par exemple, en VB.NET. Il s'agit simplement d'une philosophie différente.

Qu'est-ce que je peux faire avec eux ?

La dernière question est donc de savoir quand vous devez utiliser l'un ou l'autre. Voyons quand vous pouvez utiliser un cast explicite :

  • Conversions entre types de base.
  • Conversions de object à tout autre type (cela peut aussi inclure le déballage).
  • Conversions d'une classe dérivée vers une classe de base (ou vers 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 un casting explicite.

Voyons maintenant quand vous pouvez utiliser Convert :

  • Conversions de tout type de base vers un autre type de base (avec certaines limitations, voir MSDN ).
  • Conversions à partir de tout type qui implémente IConvertible à tout autre type (pris en charge).
  • Conversions de/vers un byte vers/depuis une chaîne de caractères.

Conclusions

OMI Convert doit être utilisé chaque fois que vous savez qu'une conversion peut échouer (à cause du format, de la plage ou parce qu'elle n'est pas prise en charge), même si la même conversion peut être effectuée avec un cast (à moins que quelque chose d'autre ne soit disponible). Il indique clairement à ceux qui liront votre code quelle est votre intention. et qu'elle peut échouer (ce qui simplifie le débogage).

Pour tout le reste, vous devez utiliser un moulage, 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 a double est quelque chose qui (surtout si le texte vient de l'utilisateur) échouera très souvent, donc vous devriez le rendre aussi explicite que possible (de plus, vous avez plus de contrôle sur lui), par exemple en utilisant un TryParse método.

Edit : quelle est la différence entre les deux ?

Selon la question mise à jour et en gardant ce que j'ai écrit auparavant (sur les quand vous pouvez utiliser un moulage par rapport à quand vous pouvez/devez utiliser Convert ) alors le dernier point à clarifier est s'il y a des différences entre eux (de plus Convert utilise IConvertible y IFormattable afin de pouvoir effectuer des opérations qui ne sont pas autorisées avec les casts).

La réponse courte est oui, ils se comportent différemment . Je vois le Convert comme une classe de méthodes d'aide, de sorte qu'elle fournit souvent un peu de avantage 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

Plutôt différent, non ? Le casting est tronqué (c'est ce à quoi on s'attend 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, il est donc impossible d'appliquer une règle générale et il faut les examiner au cas par cas... 19 types de base à convertir en tous les autres types... la liste peut être assez longue, il vaut mieux consulter MSDN au cas par cas !

13voto

Servy Points 93720

Le casting est un moyen de dire au compilateur : "Je sais que vous pensez que cette variable est un Bar, mais il se trouve que j'en sais plus que vous ; l'objet est en fait un Foo, alors laissez-moi le traiter comme si c'était un Foo à partir de maintenant". Ensuite, au moment de l'exécution, si l'objet réel s'avère être vraiment un Foo, alors votre code fonctionne, s'il s'avère que l'objet n'était pas du tout un Foo, alors vous obtenez une exception. (Plus précisément, une System.InvalidCastException .)

La conversion, quant à elle, 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 qui se trouve dans cet objet Bar. Je ne modifierai pas l'objet d'origine, je ne le traiterai pas différemment, je le ferai créer quelque chose de nouveau qui est juste basé sur une autre valeur . Quant à la façon dont il s'y prendra, cela peut être n'importe quoi. Dans le cas de Convert.ToDouble il finira par appeler Double.Parse qui possède toutes sortes de logiques complexes pour déterminer quels types de chaînes représentent quelles valeurs numériques. Vous pourriez écrire votre propre méthode de conversion qui mettrait en correspondance les chaînes de caractères avec les doubles de manière différente (peut-être pour prendre en charge une convention entièrement différente pour l'affichage des chiffres, 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 parce que le compilateur, sans votre aide, n'a aucun moyen de savoir comment mapper (par exemple) une chaîne de caractères en double. string à un double .

Alors, quand est-ce qu'on se convertit, et quand est-ce qu'on coule ? Dans les deux cas, nous avons une variable d'un certain type, disons A, et nous voulons avoir une variable de type B. Si notre objet A est vraiment, en réalité, sous le capot, un B, alors nous faisons un cast. Si ce n'est pas vraiment un B, alors nous devons le Convertir, et définir comment le programme est censé obtenir un B à partir d'un A.

6voto

user2946329 Points 10394

Desde MSDN :

Conversions explicites (casts) : Les conversions explicites nécessitent un opérateur de cast. Le cast est nécessaire quand des informations pourraient être perdues lors de la conversion, ou lorsque la conversion pourrait échouer pour d'autres raisons. Parmi les exemples typiques, citons la conversion numérique vers un type dont la précision ou l'étendue est moindre, et la conversion d'une instance de classe de base vers 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 pourraient se produire.

Vous pourriez utiliser System.Convert lorsque vous souhaitez effectuer une conversion entre non compatible types. Le site principale différence entre moulage y convertir es compiler y temps d'exécution . Les exceptions de conversion de type apparaissent à temps d'exécution c'est-à-dire qu'un cast de type qui échoue au moment de l'exécution provoquera un InvalidCastException pour être jeté.


Conclusion : Dans le casting, vous dites au compilateur que a est vraiment de type b et si c'est le cas le projet se construit sans aucune erreur comme cet exemple :

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

Mais en conversion, vous dites au compilateur qu'il existe un moyen de créer un nouvel objet à partir de a de type b s'il vous plaît faites-le et le projet se construit sans aucune erreur mais comme je l'ai dit si le cast de type échoue au moment de l'exécution, cela provoquera un InvalidCastException pour être jeté .

Par exemple, le code ci-dessous n'est jamais compilé parce que le compilateur détecte qu'il ne peut pas couler une expression de type DateTime de 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 au moment de l'exécution, vous obtiendrez InvalidCastException qui dit :

Conversion non valide de 'DateTime' en 'Int32'.

4voto

Dan Points 5254

El Convert.Double se contente en fait d'appeler en interne la méthode Double.Parse(string) método.

Ni le String ni le Double définissent une conversion explicite/implicite entre les deux types, donc le casting échouera toujours.

El Double.Parse examinera chaque caractère dans le fichier string et construisent une valeur numérique basée sur les valeurs des caractères dans le champ string . Si l'un de ces caractères n'est pas valide, le message Parse échoue (ce qui fait que la méthode Convert.Double d'échouer également).

4voto

scartag Points 9312

Dans votre exemple, vous tentez de convertir une chaîne de caractères en un double (type non intégral).

Une conversion explicite est nécessaire pour qu'il fonctionne.

Et je dois souligner 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 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 moulage et la conversion.

Votre distribution (une distribution explicite) ne répond pas aux exigences d'une distribution explicite. La valeur que vous essayez de distribuer avec l'opérateur de distribution est invalide (c'est-à-dire non intégrale).

Visitez ce site Page MSDN pour les règles de coulage / conversions

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X