69 votes

Pourquoi c# se comporte différemment sur les deux syntaxes de tableau d’int

Tableau en C# est co-variante implicitement sur le type de référence:

object[] listString = new string[] { "string1", "string2" };

Mais pas sur le type de la valeur, donc si vous changez string de int, vous aurez compilé erreur:

object[] listInt = new int[] {0, 1}; // compile error

Maintenant, le souci est lorsque vous déclarez int tableau comme deux syntaxes ci-dessous qui ne sont pas de déclarer explicitement le type d' int, juste de se différencier sur new[], le compilateur va traiter différemment:

object[] list1 = { 0, 1 };       //compile successfully
object[] list2 = new[] {0, 1};    //compile error

Vous obtiendrez object[] list1 = { 0, 1 }; compilé, mais object[] list2= new[] {0, 1}; compilé erreur.

Il semble que le compilateur C# traite

object[] list1 = { 0, 1 };

comme

object[] list1 = new object[]{ 0, 1 };

mais

object[] list2 = new[] { 0, 1 };

comme

object[] list2 = new int[]{ 0, 1 };  //error because of co-variant

Pourquoi le compilateur C# se comporte de la manière différente sur cette affaire?

57voto

Jon Points 194296

La version qui compile utilise un tableau d'initialisation pour initialiser list1. Le langage C# spec, §1.110 ("les initialiseurs de Tableau"):

Un initialiseur de tableau est constitué d'une séquence de variables initialiseurs, joint par "{"et "}" jetons et séparés par des "," jetons. Chaque variable d'initialiseur est une expression ou, dans le cas d'un un tableau multi-dimensionnel, un tableau imbriqué initialiseur.

Le contexte dans qui un initialiseur de tableau est utilisé détermine le type de la matrice en cours d'initialisation. Dans une création de la matrice d'expression, le type du tableau précède immédiatement l'initialiseur, ou est déduit à partir de la expressions dans le tableau de l'initialiseur. Dans un champ ou une variable déclaration, le type du tableau est le type du champ ou variable déclarée.

Lorsqu'un initialiseur de tableau est utilisé dans un champ ou une variable déclaration, tels que:

int[] a = {0, 2, 4, 6, 8};

c'est simplement un raccourci pour un équivalent de la création de la matrice d'expression:

int[] a = new int[] {0, 2, 4, 6, 8};

Il est donc évident que ceci devrait compiler.

La deuxième version utilise explicitement la création de la matrice d'expression, où vous devez indiquer au compilateur précisément quel type de tableau pour créer. §1.51.10.4 ("la création de la Matrice des expressions"):

Une création de la matrice de l'expression de la troisième forme est appelée implicitement tapé la création de la matrice d'expression. Il est similaire à la la deuxième forme, sauf que le type d'élément du tableau n'est pas explicitement, mais déterminée comme la meilleure de type commun (§1.50.2.14) de l'ensemble des expressions dans le tableau de l'initialiseur.

Par conséquent, la deuxième version est équivalent à

object[] list2 = new int[] { 0, 1 };

Alors maintenant, la question devient "pourquoi je ne peux pas attribuer un int[] d'un object[]", comme vous le mentionnez à la fin de la question. Et la réponse est aussi simple, donnée au §1.109 ("la Matrice de covariance"):

La matrice de covariance spécifiquement ne s'étend pas à des tableaux de types de valeur. Par exemple, aucune conversion n'existe qui permet un int[] pour être considéré comme un object[].

27voto

erikkallen Points 16601

La déclaration

object[] listInt = new int[] {0, 1};

n'est pas valide car covariante tableau de conversions ne sont pas autorisés pour les types de valeur (et int est un type de valeur). Sinon, la déclaration

object[] listInt = new string[] {"0", "1"};

est valide parce que covariante tableau de conversions sont autorisées pour les types référence. C'est parce que la cession x = (object)myString seulement implique une affectation simple, mais y = (object)myInt nécessite une boxe de l'opération.

Maintenant, sur la différence entre les deux déclarations. Dans la déclaration, object[] list2 = new[] { 0, 1 }, en raison de la façon dont l'inférence de type fonctionne, il regarde d'abord le Côté Droit de l'expression, et conclut que, new[] { 0, 1 } doivent être traités comme des new int[] { 0, 1 }. Puis il tente d'attribuer ce tableau int à un tableau d'objets, donnant une erreur à cause de la covariante de la conversion de la valeur des types de question. La déclaration object[] list1 = { 0, 1 }, cependant, utilise un initialiseur de collection, et dans ces circonstances, le type de la collection est où le type est défini, de sorte que chaque élément sera à la place de lancer pour le type attendu par la collection.

10voto

Martin Mulder Points 5326

Lorsque vous utilisez { et }, vous utilisez des initialiseurs de la collection (voir: http://msdn.microsoft.com/en-us/library/vstudio/bb384062.aspx). Les valeurs entre ces supports devront être mis quelque part. À cet effet une collection doit être créé. Le compilateur anaylize le contexte pour savoir quel type de collection.

Dans le cas où la première: object[] list1 = { 0, 1 }; il est clair qu'il faut une collection créée. Mais de quel genre? Il n'y a pas d' new fonctionnement quelque part. Il y a un seul indicateur: list1 est de type object[]. Ainsi, le compilateur crée que la collecte et le remplit avec le valiues.

Dans votre deuxième exemple object[] list1 = new[] { 0, 1 }; il y a un autre indice: new[]. Et ce soupçon est cleaer et il a également dit explicitement: "Il va y avoir un tableau. Ce tableau n'ont pas de type, donc il va essayer de trouver le type de la matrice par anaylizing les valeurs. Ce sont tous des ints'ne, il va créer un tableau de int's et le remplit. L'autre astuce object[] est totalement ignorer parce que les conseils de la création sont beaucoup plus importants que les conseils où il doit être attribué. Maintenant que le compilateur veut attribuer ce tableau liste 1 et BOOM: qui ne conviennent pas!

2voto

GM Lucid Points 93

L’instruction `` compile parce que le compilateur est assez intelligent pour savoir que vous tentez de convertir un tableau de types numériques dans un tableau de type référence, donc il boîtes les éléments Int32 dans les types référence.

Vous pourriez aussi explicitement case le type primitif :

``

Le compilateur ne fera pas implicitement de la boxe pour vous lorsque vous avez spécifié « int [] » ou « Int32 [] » comme le type de tableau, mais il semble que cela pourrait être ajouté au langage c#.

1voto

Dan Puzey Points 20794

Un tableau initialiser est un compilateur de commodité. Si je dis "je déclare un tableau d'objets et d'assigner une valeur," il est raisonnable pour le compilateur de supposer que votre { 0, 1 } est un tableau d'objets et de l'interpréter comme tel. Bien que la syntaxe semble être une affectation, il n'est pas: vous êtes à l'aide d'un initialiser. La main de cette syntaxe est - object[] list1 = new object[] { 0, 1 }

Quand vous dites new[] { 0, 1 }, c'est une expression qui crée et initialise un tableau. Cette expression est évaluée indépendamment de ce que vous êtes à l'affecter, et parce que le compilateur détecte l'implicite entier de taper, il crée un int[]. La main de la version de cette expression est - object[] list2 = new int[] { 0, 1 }

Si vous comparez la main les versions de ces deux états, il est évident de voir où ils diffèrent.

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