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:
La diffusion 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 é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 a float
:
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émente IFormattable
et qui NotSupportedException
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 avec struct
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:
- Vous pouvez 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 en un autre) donc, encore une fois, vous devez être conscient de ce que vous faites.
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:
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):
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 des IConvertible
implémentations en fait, car en C # avec un cast, vous dites au compilateur:
croyez-moi, ce type est de 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 me fiche de savoir si quelque chose sera perdu dans cette conversion.
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
-> la double
conversion, une analyse sera nécessaire). Une conversion en string
, par exemple, est toujours possible (via la ToString()
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 le string
pour produire un double
:
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 a TryParse
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émentation IConvertible
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;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
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";
double value = (string)text;
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:
- Conversions entre les types de base.
- Conversions de
object
vers tout autre type (cela peut également 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 une distribution 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 de tout type implémenté
IConvertible
vers tout autre type (pris en charge).
- Conversions depuis / vers un
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
vers double
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 une TryParse
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 plus Convert
utilise IConvertible
et IFormattable
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;
int convertedInteger = Convert.ToInt32(real);
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!