web-dev-qa-db-fra.com

Comportement de casting étrange. Impossible de convertir un objet (int) en long

J'ai le code suivant:

int intNumber1 = 100;
object intNumber2 = 100;
bool areNumberOfTheSameType = intNumber1.GetType() == intNumber2.GetType(); // TRUE
bool areEqual = intNumber1.Equals(intNumber2); // TRUE

long longNumber1 = (long) intNumber1; // OK
long longNumber2 = (long) intNumber2; // InvalidCastException. Why?

Pourquoi la deuxième distribution ne fonctionne-t-elle pas? Je me rends compte que c'est peut-être parce que l'objet n'a pas de transtypage explicite en long, mais si nous regardons son type à l'exécution, c'est System.Int32.

Si j'utilise var ou dynamic au lieu de object, cela fonctionne.

Des pensées?

45
Bashir Magomedov

La conversion de int en long est interprétée comme une conversion entre les deux types.

La conversion de object en int est interprétée comme déballant un int en boîte.

C'est la même syntaxe, mais elle dit deux choses différentes.

Dans les cas de travail (intlong, object (encadré int) → int), le compilateur sait exactement quel code produire. Si la boîte intlong devait fonctionner, le compilateur devrait en quelque sorte déterminer quelle conversion utiliser, mais il n'a pas assez d'informations pour le faire.

Voir aussi ce billet de blog d'Eric Lippert .

53
svick

object contient un type int. Mais il est considéré comme un objet (qui est un entier encadré) et un type de valeur encadré ne peut généralement être converti qu'en son type sous-jacent (le type encadré).

Pour le convertir en un autre type, vous devez d'abord le convertir en son type sous-jacent. Cela fonctionne:

long longNumber2 = (long) (int) intNumber2;

La raison pour laquelle var fonctionne est que le compilateur déduit le type au moment de la compilation. Cela signifie que lorsque vous utilisez var, le type de intNumber2 (si vous utilisez typeof) sera int. Alors que lorsque vous utilisez object, le type sera object.

L'utilisation de dynamic est un processus complètement différent et ne peut pas être comparé à var. Ici, la conversion/conversion a lieu lors de l'exécution, à l'aide de la réflexion et de la bibliothèque DLR. Il trouvera dynamiquement le type sous-jacent, trouvera qu'il a un opérateur de conversion et l'utilise.

7
Abel

(Attention: Devinez)

Int32 possède un opérateur de conversion en Int64 qui est invoqué lors de la première distribution. Object ne le fait pas, donc votre deuxième distribution essaie de convertir un objet en un autre type qui n'est pas un super-type (Int64 n'hérite pas Int32).

La raison pour laquelle cela fonctionne avec var est évidente - le compilateur vous évite simplement de taper int dans ce cas. Avec dynamic, le runtime effectue toutes les vérifications nécessaires pour ce qui doit être fait alors que normalement le compilateur insère simplement le transtypage ou appelle l'opérateur de conversion.

3
Joey

Que cela ne fonctionne pas en raison de deux types différents de transtypages (l'un convertissant, l'autre déballant) a déjà été indiqué dans les réponses ici. Ce qui pourrait être un ajout utile, c'est que Convert.ToInt64() convertira tout ce qui est soit un type intégré qui peut être converti en long, soit un type de classe qui implémente IConvertible.ToInt64(), en un long. En d'autres termes, si vous souhaitez convertir un objet contenant un entier (quelle que soit sa taille) en long, Convert.ToInt64() est le chemin à parcourir. C'est plus cher, mais ce que vous essayez de faire est plus cher que le casting, et la différence est négligeable (juste assez grande pour être inutile dans les cas où vous savez que l'objet doit être long) .

1
Jon Hanna

Vous devez décompresser le même type que celui qui a été encadré.

object intNumber2 = 100L;
// or value in the long type range
// object intNumber2 = 9223372036854775806;

long result = (long)intNumber2;
0
Peter Kelly