web-dev-qa-db-fra.com

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

J'ai une fonction qui jette une double sur string valeurs. 

string variable = "5.00"; 

double varDouble = (double)variable;

Une modification de code a été archivée et le projet a généré 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 est construit sans aucune erreur.

Quelle est la différence entre le casting et l'utilisation de la méthode Convert.To()? Pourquoi le casting jette-t-il une Exception et non le Convert.To()?

58
user5398447

Même si peut les voir comme équivalents, leur but est 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.

Il est un peu générique et équivaut en quelque sorte à un conversion parce que la conversion utilise souvent la même syntaxe qu'une conversion, de sorte que la question doit être lors d'une conversion. (implicite ou explicite) est autorisé par le langage et quand devez-vous utiliser une conversion (plus) explicite?

Laissez-moi d’abord draw une simple ligne entre eux. Formellement (même si équivalent pour la syntaxe de langage), une conversion changera de type tandis qu'une conversion changera/pourra changer de valeur (éventuellement together avec le type). De plus, une conversion est réversible alors que la conversion peut ne pas l'être.

Ce sujet est assez vaste, essayons donc de préciser un peu en excluant les opérateurs de casting personnalisés du jeu.

Castes implicites

En C #, une conversion est implicite si vous ne perdez aucune information (veuillez noter que cette vérification est effectuée avec 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 conversions sont implicites car pendant la conversion, vous ne perdrez aucune information (vous élargissez simplement le type). La conversion implicite vice versa 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), vous risquez de perdre certaines informations lors de la conversion. Par exemple, ce code ne sera pas compilé car un double peut contenir (et en fait, une valeur) non représentable avec un float:

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;

Objets

Dans le cas d'un objet (un pointeur sur), la conversion est toujours implicite lorsque le compilateur peut être sûr que le type de 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 sait que string implémente IFormattable et que NotSupportedException est (dérive de) Exception de sorte que la conversion est implicite. Aucune information n'est perdue car les objets ne changent pas de type (ceci est différent avec structs et les types primitifs car avec un transtypage, vous créez un nouvel objet d'un autre type ), quels sont vos changements view d'eux.

Casting explicite

Un transtypage est explicite lorsque la conversion n'est pas effectuée implicitement par le compilateur. Vous devez ensuite utiliser l'opérateur de transtypage. 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 en un autre), vous devez donc être conscient de ce que vous faites.

Types primitifs

Une conversion explicite est requise pour les types primitifs. 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 sont comprises dans la plage float, vous perdrez des informations (dans ce cas, la précision), de sorte que la conversion doit être explicite. Maintenant, essayez ceci:

float max = (float)Double.MaxValue;

Cette conversion échouera. Une fois encore, elle doit être explicite pour que vous en soyez conscient et que vous puissiez effectuer un contrôle (dans l'exemple, la valeur est constante mais peut provenir de calculs au moment de l'exécution ou d'E/S). Retour à 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 chiffres et c’est trop, en C #, même pour une diffusion explicite (mais cela peut être autorisé dans une autre langue).

Objets

La conversion de pointeurs (en objets) peut échouer si les types ne sont pas liés. Par exemple, ce code ne sera pas compilé (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 sera compilé, mais il risque d’échouer au moment de l’exécution (cela dépend du type effectif des objets convertis) avec un InvalidCastException:

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

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

Conversions

Donc, finalement, si les conversions sont des conversions, pourquoi avons-nous besoin de classes comme Convert? En ignorant les différences subtiles qui proviennent de Convert implémentation et de IConvertible implémentations, car en C #, avec une distribution, vous dites au compilateur:

confiance en 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 ne me soucie pas de perdre quelque chose dans cette conversion.

Pour tout le reste, une opération more == explicite est nécessaire (pensez aux implications de cast faciles , c'est pourquoi C++ a introduit long, verbose et explicite syntaxe pour eux). Cela peut impliquer une opération complexe (pour string -> double conversion, une analyse sera nécessaire). La conversion en string, par exemple, est toujours possible (via la méthode ToString()), mais elle peut signifier autre chose que ce à quoi vous vous attendez. Elle doit donc être plus explicite qu’une conversion ( 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), en utilisant des opérateurs de conversion personnalisés (définis dans la classe à convertir) ou des mécanismes plus complexes (TypeConverters ou des méthodes de classe, par exemple). Vous n'êtes pas au courant de ce qui va arriver, mais vous êtes au courant que cela peut échouer (c'est pourquoi IMO si 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 et si vous le faites, vous devriez toujours attraper l’exception qu’il peut générer (FormatException). Il est hors sujet ici, mais si lorsqu'un TryParse est disponible, vous devriez l'utiliser (car, sémantiquement, vous say , il se peut que ce ne soit pas un nombre et que ce soit encore plus rapide .. .échouer).

Les conversions en .NET peuvent provenir de nombreux endroits, TypeConverter, de conversions implicites/explicites avec des opérateurs de conversion définis par l'utilisateur, de la mise en oeuvre de IConvertible et de méthodes d'analyse (ai-je oublié quelque chose?). Jetez un coup d'oeil sur MSDN pour plus de détails à leur sujet.

Pour terminer, répondez brièvement à quelques mots sur les opérateurs de conversion définis par l'utilisateur. C’est juste sugar que le programmeur utilise un transtypage pour convertir un type en un autre. C'est une méthode dans une classe (celle qui sera lancée) qui dit "hé, s'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 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 "simplifier la vie du programmeur" et d'exposer cette conversion via un casting (souvenez-vous que ce n'est qu'un raccourci pour écrire moins). Certaines langues peuvent même permettre ceci:

double value = "123";

Permettant une conversion implicite vers 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?

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

  • Conversions entre types de base.
  • Conversions de object vers n’importe quel 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, vous n'avez donc pas le choix pour les autres et vous devez utiliser une conversion explicite.

Voyons maintenant quand vous pouvez utiliser Convert:

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

Conclusions

IMO Convert doit être utilisé chaque fois que vous savez qu'une conversion peut échouer (à cause du format, de l'étendue ou du fait qu'elle ne soit pas prise en charge), même si la même conversion peut être effectuée avec un transt (sauf si un autre élément est disponible). ). Cela indique clairement à qui lira votre code quelle est votre intention et qu'il risque d'échouer (simplification du débogage).

Pour tout le reste, vous devez utiliser un casting, vous n'avez pas le choix, mais si une autre meilleure méthode est disponible, je vous suggère de l'utiliser. Dans votre exemple, une conversion de string à double est quelque chose qui échoue très souvent (surtout si le texte provient de l'utilisateur), vous devez donc la rendre aussi explicite que possible (en outre, vous en contrôlez davantage ), par exemple en utilisant une méthode TryParse.

Edit: quelle est la différence entre eux?

Selon la question mise à jour et en conservant ce que j’écrivais auparavant (à propos de quand , vous pouvez utiliser un cast par rapport à quand vous pouvez/devez utiliser Convert), puis dernier point à clarifier. est s’il existe des différences entre elles (en outre, Convert utilise les interfaces IConvertible et IFormattable pour pouvoir effectuer des opérations non autorisées avec des conversions).

La réponse courte est oui, ils se comportent différemment . Je vois la classe Convert comme une classe de méthodes auxiliaires si souvent qu'elle fournit des comportements avantage ou 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? La distribution est tronquée (c'est ce à quoi nous nous attendions tous) mais Convert effectue un arrondi au nombre entier le plus proche (et cela peut ne pas être attendu si vous ne le savez pas). Chaque méthode de conversion introduit des différences, de sorte qu'une règle générale ne peut pas être appliquée et doit être vue au cas par cas ... 19 types de base à convertir en n'importe quel autre type ... la liste peut être assez longue, bien mieux de consulter MSDN par Cas!

99
Adriano Repetti

Casting est une façon de dire au compilateur: "Je sais que vous pensez que cette variable est un Bar, mais je connais 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 sur " Ensuite, au moment de l'exécution, si l'objet réel s'avère ê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.)

Convertir, en revanche, est une façon de dire: "Si vous me donnez un objet de type Bar, je peux créer un nouvel objet Foo qui représente ce qui se trouve dans cet objet Bar. Je ne changerai pas l'objet d'origine, il a gagné". t traiter l’objet original différemment, il va créer quelque chose de nouveau basé simplement sur une autre valeur}. Pour ce qui est de savoir comment faire, cela pourrait être n'importe quoi. Dans le cas de Convert.ToDouble, il finira par appeler Double.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 les chaînes de manière différente (peut-être pour prendre en charge une convention totalement différente pour l'affichage de nombres, tels que les chiffres romains 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 h Comment mapper (à titre d'exemple) une string à une double.

Alors, quand convertissez-vous et quand lancez-vous? Dans les deux cas, nous avons une variable de type, disons A, et nous voulons avoir une variable de type B. Si notre objet A est vraiment, sous le capot, un B, alors nous lançons. Si ce n'est pas vraiment un B, nous devons le convertir et définir comment le programme est censé obtenir un B d'un A.

10
Servy

La méthode Convert.Double appelle en fait juste en interne la méthode Double.Parse(string).

Ni le type String ni le type Double ne définissent une conversion explicite/implicite entre les deux types, ainsi la conversion échouera toujours.

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

4
Dan

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 de Convert.ToInt64 car vous pouvez perdre les fractions de la double valeur lorsque vous convertissez en int.

si votre variable a la valeur "5.25", varDouble aurait été égal à 5,00 (perte de 0,25 en raison de la conversion en Int64)

Pour répondre à votre question sur le casting vs la conversion.

Votre distribution (une distribution explicite) ne remplit pas les conditions requises pour une diffusion 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 connaître les règles de conversion/conversions

3
scartag

La conversion 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 une double en string comme ceci

double d = 5;
string s = d.ToString(); // -> "5"

// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

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

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

Ou le moyen sûr

string 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 conversion. Une diffusion est requise 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 une plage plus petite et la conversion d'une instance de classe de base en 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 transt est un moyen d'informer explicitement le compilateur que vous avez l'intention de convertir et que vous savez qu'une perte de données peut survenir.

Vous pouvez utiliser la classe System.Convert lorsque vous souhaitez convertir entre les types non compatibles. La différence principale entre transtypage et convertir est compiler et exécution. Les exceptions de conversion de type apparaissent dans run-time, c’est-à-dire qu’une conversion de type qui échoue au moment de l’exécution entraîne le lancement de InvalidCastException .


Conclusion: Lors de la conversion, vous indiquez au compilateur que a est en réalité un type b et, si tel est le cas, le projet est construit sans aucune erreur comme celle-ci:

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

Mais dans la conversion, vous dites au compilateur qu'il existe un moyen de créer un nouvel objet à partir de a de type b, veuillez le faire et projetez-le sans génération d'erreurs, mais comme je l'ai dit si la conversion du type échoue provoquera une InvalidCastException à lancer

Par exemple, le code ci-dessous n'est jamais compilé car le compilateur détecte qu'il est impossible de transtyper une expression de type DateTime en 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 aurez InvalidCastException qui dit: 

Distribution invalide de 'DateTime' à 'Int32'.

2
S.Akbari
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 transtypages explicites pour les types numériques: http://msdn.Microsoft.com/en-us/library/yht2cx7b.aspx Comme vous pouvez le constater, même tous les types numériques ne pouvaient pas être convertis en un autre type numérique. type

Quelques infos supplémentaires sur le casting ici

Et en quoi cela diffère-t-il de Convert.ToDouble ()?

Lorsque vous transformez 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 0 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 ce qui est nécessaire.

Mais lorsque vous essayez de convertir une chaîne en nombre, vous ne pouvez pas le faire car il ne suffit pas de modifier la quantité de mémoire utilisée par la variable. Par exemple, 5.00 en tant que chaîne est une séquence de "nombres": 53 (5) 46 (.) 48 (0) 48 (0) - c'est-à-dire pour ASCII, mais la chaîne contiendra quelque chose de similaire. Si le compilateur prend juste en premier N (4 pour le double - pas sûr) octets d'une chaîne - cette pièce contiendra un nombre double complètement différent. Simultanément, 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 vous attribue un nombre double, si chaîne représente un nombre. Des langages comme PHP appellent, en gros, Convert.ToDouble en arrière-plan. Mais C #, comme un langage statiquement typé, ne le fera pas pour vous. Cela vous permet de vous assurer que toutes les opérations sont sécurisées et que rien d’inattendu ne se produira:

double d = (double)"zzzz"
1
Viktor S.

double varDouble = (double)variable suppose que variable est déjà un double. Si variable n'est pas un double (c'est une chaîne), alors cela échouera .double varDouble = Convert.ToDouble(variable) fait comme il est dit - il convertit. S'il peut analyser ou extraire un double de variable, alors ce sera le cas. 

Je seconde en utilisant Double.Parse ou Double.TryParse car cela indique plus clairement ce qui est censé se passer. Vous commencez avec une chaîne et vous attendez à ce qu'elle soit convertible en double. En cas de doute, utilisez TryParse

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

1
Scott Hannen

La conversion d'une chaîne en un double comme celui-ci n'est pas autorisée. C #, c'est pourquoi vous obtenez une exception. La chaîne doit être convertie ( MSDN doc qui indique les chemins de conversion acceptés). 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 NULL). 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 le cas, il retournera cette valeur. 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 existe Parse qui est en grande partie similaire à Convert, mais vous devriez également regarder TryParse qui vous permettrait de:

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'utiliser Convert ou Parse une chaîne non numérique.

1
Keen

La différence la plus importante est que si type casting est utilisé et que la conversion échoue (Disons que nous convertissons une très grande valeur float en int) aucune exception ne sera levée et la valeur minimale qu'un int peut conserver montré . Mais si vous utilisez Convert , une exception sera levée pour de tels scénarios.

0
TomHardy