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 !