List<int> test = {1, 2, 3} - est-il une fonction ou un bug?
Comme vous le savez, il n'est pas autorisé à utiliser le Tableau-initialisation de la syntaxe avec les Listes. Il va donner une erreur de compilation. Exemple:
List<int> test = { 1, 2, 3}
//At compilation the following error is shown:
//Can only use array initializer expressions to assign to array types.
Cependant aujourd'hui je n'ai la suite (très simplifié):
class Test
{
public List<int> Field;
}
List<Test> list = new List<Test>
{
new Test { Field = { 1, 2, 3 } }
};
Le code ci-dessus compile très bien, mais lorsqu'il est exécuté, il va donner une "références de l'Objet n'est pas définie pour un objet" erreur d'exécution.
Je m'attends à ce que le code à donner à une erreur de compilation. Ma question est la suivante: Pourquoi n'est-ce pas, et il y a toutes les bonnes raisons pour quand un tel scénario permettrait d'exécuter correctement?
Cela a été testé à l'aide .NET 3.5, les deux .Net et Mono compilateurs.
Acclamations.
Test test= new Test { Field = { 1, 2, 3 }};
a le même comportement."Les références de l'Objet n'est pas définie pour un objet" est tout à fait logique, puisque votre Champ de liste n'est pas initialisé. Le Test organisme public List<int> Champ = new List<int>(); fait de cette course sans problème également.
prendre un réflecteur, et de voir comment le compilateur traite cela, et vous allez comprendre. aussi voir comment le var truc = new List<int>() {4,5}; est handeled
OriginalL'auteur mikabytes | 2011-02-04
Vous devez vous connecter pour publier un commentaire.
Je pense que c'est une conception de comportement. Le
Test = { 1, 2, 3 }
est compilé en code qui appelleAdd
méthode de la liste stockée dans laTest
champ.La raison pour laquelle vous vous êtes
NullReferenceException
est queTest
estnull
. Si vous initialisez laTest
champ à une nouvelle liste, puis le code du travail:Il est tout à fait logique - si vous écrivez
new List<int> { ... }
puis il crée une nouvelle instance de la liste. Si vous n'ajoutez pas de construction de l'objet, il va utiliser l'instance existante (ounull
). Aussi loin que je peux voir, C# spec ne contient pas explicite des règles de traduction qui pourrait correspondre à ce scénario, mais il donne un exemple (voir Section 7.6.10.3):Un
List<Contact>
peut être créé et initialisé de la façon suivante:qui a le même effet que
où
__c1
et__c2
sont des variables temporaires qui sont invisibles et inaccessibles.Merci, excellente réponse. Avec cette description du comportement est logique. Cependant, ce qu'il ne semble pas logique pour moi est de permettre = {} la syntaxe pour ajouter des éléments, de cette façon je pourrais lire "Ce champ est égale à", mais en réalité, la liste peut contenir plus d'éléments que ceux que j'ai précisé si toutes ont été ajoutés lors de la construction de l'objet. Je préférerais avoir une erreur de compilation 😉
Oui, il y a un peu d'incohérence. À l'aide de la logique à partir de cet échantillon, on devrait être capable d'écrire
var l = new List<int>(); l = { 1, 2, 3 };
- mais cela ne fonctionne pas!OriginalL'auteur Tomas Petricek
Depuis votre attente est contraire à la fois à la spécification et la mise en œuvre, votre attente va aller inassouvi.
Parce que la spécification précise qui est légal dans la section 7.6.10.2, que je cite ici pour plus de commodité:
Que la spec dit, les éléments donnés dans l'initialiseur, sont ajoutés à la collection référencé par la propriété. La propriété n'a pas de référence à une collection; il est nul. Par conséquent, à l'exécution, il donne une référence nulle exception. Quelqu'un a pour initialiser la liste. Je voudrais vous recommandons de changer le "Test" de la classe, de sorte que son constructeur initialise la liste.
Requêtes LINQ besoin expressions, non pas des déclarations. L'ajout d'un membre à une nouvelle collection dans une nouvelle liste nécessite l'appel "Ajouter". Depuis "Ajouter" est nulle-retour, un appel à celle-ci ne peut apparaître dans une expression de déclaration. Cette fonctionnalité vous permet de créer une nouvelle collection ("nouveau") et de le remplir, ou de remplir une collection existante (sans les "nouvelles"), où la collection est un membre d'un objet que vous créez comme le résultat d'une requête LINQ.
=
opération à faire une exacte d'affectation. Votre LINQ motivation contribue à cela, et je peux voir pourquoi ils ont décidé de le concevoir de cette façon.Ajouter à la documentation MSDN à l'adresse msdn.microsoft.com/en-us/library/bb384062.aspx serait bien. Il ne montre que d'autres variantes de la collection/objet-les initialiseurs.
le sens de
=
dans votre code correspond à la=
normal initialiseurs de la collection. La chose inhabituelle sur votre code, c'est qu'il n'a pas denew List<T>
.voir cette question pour un peu plus de réflexions à propos de cette syntaxe. stackoverflow.com/questions/4773889/...
L'insolite chose que
=
dans ce contexte, signifie "ajouter ce pour moi". Oui, il les matchs, car il est fait de la même façon dans les deux cas.OriginalL'auteur Eric Lippert
Ce code:
Est traduit:
Depuis
Field
estnull
, vous obtenez leNullReferenceException
.Ce qui est appelé un initialiseur de collection, et il va travailler dans votre premier exemple si vous faites cela:
Vous avez vraiment besoin de nouveau quelque chose pour être en mesure d'utiliser cette syntaxe, c'est à dire, un initialiseur de collection ne peut apparaître que dans le cadre d'une création de l'objet de l'expression. Dans le C# spec, section 7.6.10.1, c'est la syntaxe pour une création de l'objet de l'expression:
Donc, tout commence avec un
new
expression. L'intérieur de l'expression, vous pouvez utiliser un initialiseur de collection sans lenew
(section 7.6.10.2):Maintenant, ce que vous êtes vraiment en manque, c'est une sorte de liste littérale, ce qui serait vraiment pratique. J'ai proposé l'un de ces littéral pour enumerables ici.
Votre dernier exemple est bien connu. Mais je ne savais pas qu'on pouvait utiliser une collection d'initialiseur de l'intérieur d'un objet-l'initialiseur sans
new MyCollection
. Et l'article MSDN vous lié ne pas mentionner que ce soit.Une autre bonne réponse. Je suppose qu'ils ont eu à le faire de cette façon à permettre l'ajout d'éléments sans écraser les valeurs qui peuvent avoir été ajoutés dans le constructeur de l'objet initial. C'est un peu ambiguë, car vous vous attendez à la valeur résultante d'un
=
opération à une correspondance exacte de la droite, mais dans ce cas, il est seulement l'ajout d'un ensemble de valeurs.vous avez à regarder du C# grammaire pour . J'ai mis à jour de façon appropriée.
OriginalL'auteur Jordão
il compile parce qu'il est valable en C#, il échoue au moment de l'exécution, parce qu'il n'a pas instancier l'objet de la liste
ensuite, nous allons demander pourquoi la division par zéro compile et comment se fait-compilateur C# n'a pas saisi, à droite
Ivanov: Vous voudrez peut-être essayer de répondre à la question dans votre réponse, au lieu de dans les commentaires. Bien sûr, d'autres ont répondu à l'améliorer. Et pour information, le compilateur c# ne capture de base de la division par zéro des erreurs. par exemple
int i = 1 / 0;
ne compile pas.Kepp: À mon avis, la racine de la réponse n'est pas qu'il n'a pas instancier l'objet de la liste, mais que le compilateur a tourné la Liste = Array type de chose dans l'initialiser dans une série de séparer ajoute plutôt que de simplement traiter comme une affectation unique. Si cela est évident pour vous, alors grand, mais à en juger par le nombre de personnes qui upvoted la question, il n'était clairement pas évident pour tous.
OriginalL'auteur Kris Ivanov
La raison pour cela est que le deuxième exemple est une liste de membres d'initialiser et de la MemberListBinding expression à partir du Système.Linq.Les Expressions de donner un aperçu de ce référer à ma réponse à cette autre question pour plus de détails: Quels sont quelques exemples de MemberBinding expressions LINQ?
Ce type d'initialiser exige que la liste est déjà initialisé, de sorte que la séquence que vous fournissez peuvent être ajoutés.
Tant du point de vue syntaxique il n'y a absolument rien de mal avec le code - le
NullReferenceException
est un runtime erreur causée par la Liste de ne pas avoir réellement été créé. Un constructeur par défaut quinew
s de la liste, ou une lignenew
dans le code du corps, permettra de résoudre l'erreur d'exécution.Quant à savoir pourquoi il ya une différence entre cela et la première ligne de code dans votre exemple, il n'est pas permis parce que ce type d'expression ne peut pas être sur le côté droit d'une affectation parce que ne fait pas créer quoi que ce soit, c'est seulement un raccourci pour
Add
.OriginalL'auteur Andras Zoltan
Modifier votre code:
La raison en est que vous devez créer explicitement un objet de collection avant de vous pouvez mettre des articles.
Il compile parce que c'est correct. Ce que vous voulez entendre?
Vous pourriez nous expliquer pourquoi l'OP du code compile.
Pegram - il est vrai - de la syntaxe utilisée nécessite la Liste d'initialisation d'abord que c'est un membre de membre de l'initialiser.
OriginalL'auteur Al Kepp