Pourquoi C # se comporte différemment sur deux syntaxes de tableau int

Le tableau en C # est co-variant implicitement sur le type de référence :

object[] listSsortingng = new ssortingng[] { "ssortingng1", "ssortingng2" }; 

Mais pas sur le type de valeur, donc si vous changez de ssortingng en int , vous obtiendrez une erreur compilée:

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

Maintenant, le problème est que lorsque vous déclarez int array comme deux syntaxes ci-dessous qui ne déclarent pas explicitement le type int , juste juste différencier sur new[] , le compilateur traitera différemment:

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

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

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 différemment dans ce cas?

La version qui comstack utilise un initialiseur de tableau pour initialiser list1 . La spécification du langage C #, § 1.110 (“Initialiseurs de tableaux”) indique:

Un initialiseur de tableau consiste en une séquence d’initialiseurs de variables, entourée de jetons «{» et «}» et séparés par des jetons «,». Chaque initialiseur de variable est une expression ou, dans le cas d’un tableau multidimensionnel, un initialiseur de tableau nested.

Le contexte dans lequel un initialiseur de tableau est utilisé détermine le type du tableau initialisé. Dans une expression de création de tableau, le type de tableau précède immédiatement l’initialiseur ou est déduit des expressions de l’initialiseur de tableau. Dans un champ ou une déclaration de variable, le type de tableau est le type du champ ou de la variable en cours de déclaration.

Lorsqu’un initialiseur de tableau est utilisé dans une déclaration de champ ou de variable, par exemple:

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

c’est simplement un raccourci pour une expression de création de tableau équivalente:

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

Il est donc évident que cela devrait être compilé.

La deuxième version utilise une expression de création de tableau explicite, où vous indiquez au compilateur quel type de tableau créer. §1.51.10.4 (“Expressions de création de tableaux”):

Une expression de création de tableau de la troisième forme est appelée expression de création de tableau implicitement typée . Il est similaire à la seconde forme, sauf que le type d’élément du tableau n’est pas explicitement donné, mais qu’il est défini comme le meilleur type commun (§1.50.2.14) de l’ensemble des expressions dans l’initialiseur de tableau.

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

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

La question devient donc maintenant “pourquoi ne puis-je pas assigner un int[] à un object[] “, comme vous le mentionnez à la fin de la question. Et la réponse est aussi simple, donnée au § 1.109 (“covariance des tableaux”):

La covariance des tableaux ne s’étend pas spécifiquement aux tableaux de types valeur. Par exemple, il n’existe aucune conversion permettant à un int[] d’être traité comme un object[] .

La déclaration

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

n’est pas valide car les conversions de tableau covariant ne sont pas autorisées pour les types de valeur (et int est un type de valeur). Alternativement, la déclaration

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

est valide car les conversions de tableau covariant sont autorisées pour les types de référence. C’est parce que l’affectation x = (object)mySsortingng n’implique qu’une simple affectation, mais y = (object)myInt nécessite une opération de boxe.

Maintenant, à la différence entre les deux déclarations. Dans l’ object[] list2 = new[] { 0, 1 } déclaration object[] list2 = new[] { 0, 1 } , en raison du fonctionnement de l’inférence de type, il examine d’abord l’expression du côté droit et conclut que new[] { 0, 1 } devrait être traité comme new int[] { 0, 1 } . Ensuite, il essaie d’affecter ce tableau int à un tableau d’objects, ce qui génère une erreur en raison de la conversion des covariants des types de valeurs. La déclaration object[] list1 = { 0, 1 } , cependant, utilise un initialiseur de collection, et dans ce cas, le type de la collection est l’endroit où le type est défini, donc chaque élément sera converti dans le type attendu par la collection. .

Lorsque vous utilisez { et } , vous utilisez des initialiseurs de collection (voir: http://msdn.microsoft.com/en-us/library/vstudio/bb384062.aspx ). Les valeurs entre ces parenthèses devront être mises quelque part. Pour cela, une collection doit être créée. Le compilateur parsingra le contexte pour savoir quel type de collection.

Dans le cas du premier: object[] list1 = { 0, 1 }; il est clair qu’il devrait y avoir une collection créée. Mais quel genre devrait-il être? Il n’y a pas de new opération quelque part. Il n’y a qu’un seul indice: list1 est de type object[] . Ainsi, le compilateur crée cette collection et la remplit avec les valeurs.

Dans votre deuxième exemple d’ object[] list1 = new[] { 0, 1 }; il y a un autre indice: new[] . Et cette indication dit explicitement: Il va y avoir un tableau. Ce tableau n’a pas de type, il va donc essayer de trouver le type du tableau en analysant les valeurs. Ce sont tous des int donc il va créer un tableau de int et le remplit. L’autre object[] conseil object[] est totalement ignoré car les indications de création sont beaucoup plus importantes que les indications auxquelles il doit être assigné. Maintenant, le compilateur veut assigner ce tableau à list1 et BOOM: ça ne va pas!

La déclaration object[] list1 = { 0, 1 }; comstack parce que le compilateur est assez intelligent pour savoir que vous tentez de convertir un tableau de types numériques en un tableau de type référence, il encapsule donc les éléments Int32 dans les types de référence.

Vous pouvez aussi inclure explicitement le type primitif:

object[] list2 = Array.ConvertAll(new[] { 0, 1 }, input => (Object)input);

Le compilateur ne fera pas implicitement la boxe pour vous lorsque vous avez spécifié “int []” ou “Int32 []” comme type de tableau, mais il semble que cela puisse être ajouté à C #.

Un initialiseur de tableau est pratique pour le compilateur. Si je dis “je déclare un tableau d’objects et lui atsortingbue une valeur”, il est raisonnable que le compilateur suppose que votre { 0, 1 } est un tableau d’objects et l’interprète comme tel. Bien que la syntaxe semble être une affectation, ce n’est pas le cas: vous utilisez un initialiseur. Le object[] list1 = new object[] { 0, 1 } de cette syntaxe est object[] list1 = new object[] { 0, 1 }

Quand vous dites new[] { 0, 1 } , c’est une expression qui crée un tableau et l’initialise. Cette expression est évaluée indépendamment de ce que vous lui assignez – et comme le compilateur détecte le typage entier implicite, il crée un int[] . La version longue de cette expression est object[] list2 = new int[] { 0, 1 }

Si vous comparez les versions à long terme de ces deux déclarations, il est clair de voir où elles diffèrent.

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

est sténographie pour

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

qui ne fonctionne pas car int[] n’est pas covariant avec object[] .

Et quand vous dites new[] , cela équivaut à new int[] , donc la même chose s’applique.