48 votes

Est-ce que caster est la même chose que convertir?

Dans le livre Learning C# de Jesse Liberty, il dit

"Les objets d'un type peuvent être convertis en objets d'un autre type. C'est ce qu'on appelle le "casting".

Si vous examinez le code IL généré ci-dessous, vous pouvez clairement voir que l'assignation castée ne fait pas la même chose que l'assignation convertie. Dans le premier cas, vous pouvez voir le boxing/unboxing se produire ; dans le second, vous pouvez voir un appel à une méthode de conversion.

Je sais qu'à la fin cela peut être simplement une différence sémantique stupide -- mais le casting est-il juste un autre mot pour convertir. Je ne veux pas être sarcastique, mais je ne suis pas intéressé par l'intuition de quiconque à ce sujet -- les opinions ne comptent pas ici ! Est-ce que quelqu'un peut indiquer une référence définitive qui confirme ou nie si le casting et la conversion sont la même chose ?

    object x;
    int y;

    x = 4;

    y = ( int )x;

    y = Convert.ToInt32( x );

Merci

rp

Note ajoutée après le commentaire de Matt sur explicite/implicite :

Je ne pense pas que l'implicite/explicite soit la différence. Dans le code que j'ai posté, le changement est explicite dans les deux cas. Une conversion implicite est ce qui se produit lorsque vous assignez un short à un int.

Remarque à Sklivvz :

Je voulais une confirmation que mes soupçons sur la négligence de la langue de Jesse Liberty (habituellement lucide et claire) étaient corrects. Je pensais que Jesse Liberty était un peu laxiste dans son langage. Je comprends que le casting est lié à la hiérarchie d'objets -- c'est-à-dire, vous ne pouvez pas caster un entier en une chaîne de caractères mais vous pourriez caster une exception personnalisée dérivée de System.Exception en System.Exception.

C'est intéressant, cependant, que lorsque vous essayez de caster un int en une chaîne de caractères, le compilateur vous dit qu'il n'a pas pu "convertir" la valeur. Peut-être que Jesse a plus raison que ce que je pensais !

30voto

Sklivvz Points 16412

Absolument pas!

Convert essaie de vous obtenir un Int32 par "tous les moyens possibles". Cast ne fait rien de tel. Avec cast, vous dites au compilateur de traiter l'objet comme un Int, sans conversion.

Vous devez toujours utiliser cast lorsque vous savez (par conception) que l'objet est un Int32 ou une autre classe qui a un opérateur de casting vers Int32 (comme float, par exemple).

Convert devrait être utilisé avec String, ou avec d'autres classes.

Essayez ceci

static void Main(string[] args)
{
    long l = long.MaxValue;

    Console.WriteLine(l);

    byte b = (byte) l;

    Console.WriteLine(b);

    b = Convert.ToByte(l);

    Console.WriteLine(b);

}

Résultat:

9223372036854775807

255

Unhandled Exception:

System.OverflowException: Value is greater than Byte.MaxValue or less than Byte.MinValue at System.Convert.ToByte (Int64 value) [0x00000] at Test.Main (System.String[] args) [0x00019] in /home/marco/develop/test/Exceptions.cs:15

18voto

Jon Skeet Points 692016

La réponse simple est : cela dépend.

Pour les types de valeur, le cast impliquera généralement une véritable conversion vers un type différent. Par exemple :

float f = 1,5f;
int i = (int) f; // Conversion

Lorsque l'expression de cast se déballe, le résultat (en supposant que cela fonctionne) est généralement juste une copie de ce qui était dans la boîte, avec le même type. Il y a cependant des exceptions - vous pouvez se déballer d'un int boxé vers un enum (avec un type sous-jacent de int) et vice versa; de même, vous pouvez se déballer d'un int boxé vers un Nullable.

Lorsque l'expression de cast est d'un type de référence à un autre et qu'aucune conversion définie par l'utilisateur n'est impliquée, il n'y a pas de conversion en ce qui concerne l'objet lui-même - seulement le type de la référence "change" - et ce n'est vraiment que la façon dont la valeur est considérée, plutôt que la référence elle-même (qui seront les mêmes bits qu'auparavant). Par exemple :

object o = "hello";
string x = (string) o; // Aucune donnée n'est "convertie"; x et o font référence au même objet

Lorsque des conversions définies par l'utilisateur entrent en jeu, cela implique généralement de retourner un objet/valeur différent. Par exemple, vous pourriez définir une conversion en string pour votre propre type - et ça ne serait certainement pas les mêmes données que celles de votre propre objet. (Il se peut que ce soit déjà une chaîne existante référencée depuis votre objet, bien sûr.) Dans mon expérience, les conversions définies par l'utilisateur existent généralement entre des types de valeur plutôt que des types de référence, donc ce n'est rarement un problème.

Tout cela compte comme des conversions en termes de spécification - mais elles ne comptent pas toutes comme convertir un objet en un objet d'un type différent. Je soupçonne que c'est un cas où Jesse Liberty est imprécis avec la terminologie - j'ai remarqué ça dans Programming C# 3.0, que je viens de lire.

Cela couvre-t-il tout ?

7voto

Wayne Bloss Points 1464

La meilleure explication que j'ai vue peut être vue ci-dessous, suivie d'un lien vers la source :

"... La vérité est un peu plus complexe que ça. .NET fournit trois méthodes pour aller de point A à point B, pour ainsi dire.

Premièrement, il y a la conversion implicite. C'est la conversion qui ne nécessite rien de plus qu'une affectation :

int i = 5;
double d = i;

Celles-ci sont également appelées "conversions d'élargissement" et .NET vous permet de les effectuer sans aucun opérateur de conversion car vous ne risquez jamais de perdre d'information : la plage possible de valeurs valides d'un double englobe la plage de valeurs valides pour un int et même un peu plus, donc vous ne risquez jamais de faire cette affectation et de découvrir avec horreur que le runtime a supprimé quelques chiffres de votre valeur int. Pour les types de référence, la règle derrière une conversion implicite est que la conversion ne peut jamais lancer de InvalidCastException : il est clair pour le compilateur que la conversion est toujours valide.

Vous pouvez créer de nouveaux opérateurs de conversion implicite pour vos propres types (ce qui signifie que vous pouvez faire des conversions implicites qui ne respectent aucune règle, si vous êtes stupide à ce sujet). La règle de base est qu'une conversion implicite ne peut jamais inclure la possibilité de perdre de l'information lors de la transition.

Remarquez que la représentation sous-jacente a bel et bien changé dans cette conversion: un double est représenté complètement différemment d'un int.

Le deuxième type de conversion est une conversion explicite. Une conversion explicite est requise lorsque qu'il y a une possibilité de perte d'information, ou une possibilité que la conversion puisse ne pas être valide et donc lancer une InvalidCastException :

double d = 1.5;
int i = (int)d;

Ici vous allez évidemment perdre de l'information : i sera 1 après la conversion, donc le 0.5 est perdu. Cela est également appelé une conversion "rétrécissante", et le compilateur exige que vous incluiez une conversion explicite (int) pour indiquer que oui, vous savez que de l'information peut être perdue, mais que cela ne vous dérange pas.

De même, avec les types de référence, le compilateur exige des conversions explicites dans les situations où la conversion peut ne pas être valide au moment de l'exécution, comme un signal que oui, vous savez qu'il y a un risque, mais que vous savez ce que vous faites.

Le troisième type de conversion est celui qui implique un changement aussi radical dans la représentation que les concepteurs n'ont pas même prévu une conversion explicite : ils vous obligent à appeler une méthode pour faire la conversion :

string s = "15";
int i = Convert.ToInt32(s);

Remarquez qu'il n'y a rien qui exige absolument un appel à une méthode ici. Les conversions implicites et explicites sont aussi des appels de méthode (c'est ainsi que vous créez les vôtres). Les concepteurs auraient tout à fait pu créer un opérateur de conversion explicite qui convertit une string en int. L'exigence d'appeler une méthode est un choix stylistique plutôt qu'une exigence fondamentale du langage.

Le raisonnement stylistique est le suivant : String-to-int est une conversion compliquée avec de nombreuses opportunités pour tout aller horriblement mal :

string s = "The quick brown fox";
int i = Convert.ToInt32(s);

En tant que tel, l'appel de méthode vous donne de la documentation à lire, et un indice large que c'est quelque chose de plus qu'une simple conversion rapide.

Lors de la conception de vos propres types (en particulier vos propres types de valeur), vous pouvez décider de créer des opérateurs de conversion et des fonctions de conversion. Les lignes qui séparent les territoires de la "conversion implicite", "conversion explicite" et "fonction de conversion" sont un peu floues, donc différentes personnes peuvent prendre différentes décisions sur ce qui devrait être quoi. Essayez simplement de garder à l'esprit la perte d'information, et le risque d'exceptions et de données invalides, et cela devrait vous aider à décider."

  • Bruce Wood, 16 novembre 2005

http://bytes.com/forum/post1068532-4.html

2voto

David B Points 53123

Le casting implique des références

List myList = new List();
//up-cast
IEnumerable myEnumerable = (IEnumerable) myList;
//down-cast
List myOtherList = (List) myEnumerable;

Remarquez que les opérations effectuées sur myList, telles que l'ajout d'un élément, se reflètent dans myEnumerable et myOtherList. Cela est dû au fait qu'ils sont toutes des références (de types différents) à la même instance.

Le up-casting est sûr. Le down-casting peut générer des erreurs d'exécution si le programmeur a commis une erreur dans le type. Le down-casting sûr dépasse le cadre de cette réponse.

La conversion implique des instances

List myList = new List();
int[] myArray = myList.ToArray();

myList est utilisée pour produire myArray. Il s'agit d'une conversion non destructive (myList fonctionne parfaitement bien après cette opération). Remarquez également que les opérations effectuées sur myList, telles que l'ajout d'un élément, ne se reflètent pas dans myArray. Cela est dû au fait qu'ils sont des instances complètement séparées.

decimal w = 1.1m;
int x = (int)w;

Il existe des opérations utilisant la syntaxe de cast en C# qui sont en réalité des conversions.

1voto

Mark T Points 1276

Sémantiquement parlant, un test rapide montre qu'ils ne sont PAS équivalents !
Ils effectuent la tâche différemment (ou peut-être qu'ils effectuent des tâches différentes).

x=-2.5 (int)x=-2 Convert.ToInt32(x)=-2
x=-1.5 (int)x=-1 Convert.ToInt32(x)=-2
x=-0.5 (int)x= 0 Convert.ToInt32(x)= 0
x= 0.5 (int)x= 0 Convert.ToInt32(x)= 0
x= 1.5 (int)x= 1 Convert.ToInt32(x)= 2
x= 2.5 (int)x= 2 Convert.ToInt32(x)= 2

Remarquez les cas x=-1.5 et x=1.5.

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