9 votes

Le créateur de dictionnaire a un comportement différent et provoque une exception d'exécution lorsqu'il est utilisé en combinaison avec un initialisateur de tableau.

J'ai le code C# suivant qui initialise un nouveau dictionnaire avec des clés int et des valeurs List:

var dictionnaire =
    new Dictionary>
    {
        [1] = new List { "str1", "str2", "str3" },
        [2] = new List { "str4", "str5", "str6" }
    };

Si je décompile un exécutable fait à partir de cet extrait en C#, la partie correspondante ressemble à ceci :

Dictionary> expr_06 = new Dictionary>();
expr_06[1] = new List
{
    "str1",
    "str2",
    "str3"
};
expr_06[2] = new List
{
    "str4",
    "str5",
    "str6"
};

Tout semble normal et fonctionne correctement ici.

Mais lorsque j'ai le code suivant :

var dictionnaire2 =
    new Dictionary>
    {
        [1] = { "str1", "str2", "str3" },
        [2] = { "str4", "str5", "str6" }
    };

ce qui semble encore être un code normal et compile avec succès, mais pendant l'exécution je reçois l'exception suivante :

System.Collections.Generic.KeyNotFoundException: 'La clé spécifiée est introuvable dans le dictionnaire.'

Quand je regarde le code décompilé du deuxième exemple, je peux voir qu'il est différent du premier :

Dictionary> expr_6E = new Dictionary>();
expr_6E[1].Add("str1");
expr_6E[1].Add("str2");
expr_6E[1].Add("str3");
expr_6E[2].Add("str4");
expr_6E[2].Add("str5");
expr_6E[2].Add("str6");

Et bien sûr cela explique l'exception.

Maintenant mes questions sont les suivantes :

  1. Ce comportement est-il attendu et est-il documenté quelque part ?

  2. Pourquoi la syntaxe ci-dessus est autorisée mais la syntaxe suivante ne l'est pas ?

    List liste = { "test" };
  3. Pourquoi la syntaxe suivante n'est-elle pas autorisée alors ?

    var dict = new Dictionary
    {
        [1] = { "test1", "test2", "test3" },
        [2] = { "test4", "test5", "test6" }
    };

Questions similaires mais différentes :

5voto

Adrian Points 8690

Laissez-moi essayer de répondre à toutes vos questions :


  1. Ceci est-il un comportement attendu et est-il documenté quelque part ?

Oui, c'est documenté dans la spécification du langage C# 6.0 aux sections §7.6.11.2 Initialisateurs d'objets et §7.6.11.3 Initialisateurs de collections.

La syntaxe

var a =
    new Test
    {
        [1] = "foo"
        [2] = "bar"
    };

a en réalité été nouvellement introduite en C# 6.0 en tant qu'extension de la syntaxe précédente d'initialisation d'objets aux indexeurs. Un initialisateur d'objet utilisé avec new (voir expression de création d'objet, §7.6.11) se traduit toujours par une instantiation d'objet et un accès aux membres de l'objet correspondant (en utilisant une variable temporaire), dans ce cas :

var _a = new Test();
_a[1] = "foo";
_a[2] = "bar";
var a = _a;

L'initialiseur de collection fonctionne de manière similaire à ceci, sauf que chaque élément de l'initialiseur est passé en tant qu'argument à la méthode Add de la collection nouvellement créée :

var list = new List {1, 2};

devient

var _list = new List();
_list.Add(1);
_list.Add(2);
var list = _list;

Un initialisateur d'objet peut également contenir d'autres initialisateurs d'objets ou de collections. La spécification précise le cas des initialisateurs de collections :

Un initialiseur de membre qui spécifie un initialiseur de collection après le signe égal est une initialisation d'une collection embarquée. Au lieu d'assigner une nouvelle collection au champ, propriété ou indexeur cible, les éléments donnés dans l'initialiseur sont ajoutés à la collection référencée par la cible.

Un seul initialisateur de collection utilisé dans un initialisateur d'objet ne tentera pas de créer une nouvelle instance de collection. Il essaiera uniquement d'ajouter les éléments à une collection existante, c'est-à-dire une collection qui a déjà été instanciée dans le constructeur de l'objet parent.

Écrire

[1] = new List { "str1", "str2", "str3" }

est en fait un cas totalement différent car il s'agit d'une expression de création d'objet qui ne contient qu'un initialisateur de collection, mais n'en est pas un.


  1. Pourquoi la syntaxe ci-dessus est-elle autorisée mais la syntaxe suivante ne l'est pas ?

    List list = { "test" };

Maintenant, ce n'est plus un initialisateur de collection. Un initialisateur de collection ne peut apparaître que dans un initialisateur d'objet ou dans une expression de création d'objet. Un simple { obj1, obj2 } à côté d'une affectation est en fait un initialisateur de tableau (§12.6). Le code ne compile pas car vous ne pouvez pas assigner un tableau à une List.


  1. Pourquoi la syntaxe suivante n'est-elle pas autorisée alors ?

    var dict = new Dictionary 
    {
        [1] = { "test1", "test2", "test3" },
        [2] = { "test4", "test5", "test6" }     
    };

Ce n'est pas autorisé car les initialiseurs de collection ne sont autorisés que pour initialiser des collections, pas des types de tableau (car seules les collections ont une méthode Add).

3voto

HimBromBeere Points 8328

En utilisant un initialisateur de liste (c'est-à-dire en utilisant = { "test1", "test2", "test3" }), on suppose que la liste a déjà été initialisée d'une manière ou d'une autre, par exemple dans le constructeur de la classe comme ceci :

class MyClass
{
    List TheList = new List();
}

Maintenant, vous pouvez utiliser l'initialisateur de liste :

var m = new MyClass { TheList = { mesElementsIci } };

Comme vous l'avez déjà montré, il s'agit simplement d'un raccourci pour appeler la méthode Add.

Cependant, dans votre cas, la liste n'est pas du tout initialisée, ce qui fait que l'appel à l'initialisateur de liste génère l'exception mentionnée. Déclarer simplement un Dictionary dont les valeurs sont des listes de n'importe quel type ne signifie pas que vous avez déjà initialisé ces listes. Vous devez le faire comme avec n'importe quelle autre classe avant de pouvoir appeler l'une de ses méthodes (par exemple, Add qui est simplement encapsulé dans l'initialiseur).

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