2009-09-23 9 views
10

Le code suivant compile, mais échoue avec un NullReferenceException:C# dictionnaire initialiseur compilation inconsistance

class Test 
{ 
    public Dictionary<string, string> Dictionary { get; set; } 
} 

static void Main(string[] args) 
{ 
    var x = new Test 
    { 
     Dictionary = // fails 
     { 
      { "key", "value" }, { "key2", "value2" } 
     } 
    }; 
} 

Si vous remplacez la ligne marquée 'échoue' avec ce qui suit, il fonctionne (comme prévu):

Dictionary = new Dictionary<string, string> 

La syntaxe défaillante peut-elle être utilisée - peut-elle être utilisée avec succès dans un autre cas? Ou est-ce un oubli dans le compilateur?

Répondre

32

Non, ce n'est pas une erreur ... c'est une faille dans votre compréhension de la syntaxe d'initialisation :)

L'idée du

Dictionary = { ... } 

est pour les cas où l'appelant a lu accès à une propriété de collection, mais pas écrire accès. En d'autres termes, des situations comme celle-ci:

class Test 
{ 
    private readonly Dictionary<string, string> dictionary 
     = new Dictionary<string, string>(); 
    public Dictionary<string, string> Dictionary { get { return dictionary; } } 
} 

Fondamentalement, il finit par être des appels à Add, mais sans créer une nouvelle collection en premier. Donc, ce code:

Test test = new Test { Dictionary = { { "a", "b"}, {"c", "d" } }; 

est équivalent à:

Test tmp = new Test(); 
Dictionary<string, string> tmpDictionary = tmp.Dictionary; 
tmpDictionary.Add("a", "b"); 
tmpDictionary.Add("c", "d"); 
Test test = tmp; 

Un bon exemple de ce qui est utile est la collection Controls pour une interface utilisateur. Vous pouvez le faire:

Form form = new Form 
{ 
    Controls = 
    { 
     new Button { Text = "Hi" }, 
     new TextBox { Text = "There" } 
    } 
}; 

mais vous ne pouviez pas vraiment mettre la propriété Controls, parce qu'il est en lecture seule.

+0

Alors il est utilisé pour ajouter des éléments à un dictionnaire créé par le constructeur - j'aurais dû m'en rendre compte. Mais c'est une utilisation étrange de l'opérateur égal, puisque l'effet est d'ajouter à ce qui est déjà dans le dictionnaire (le constructeur peut avoir ajouté des éléments en premier). –

+0

Sorte de, oui ... mais en même temps, il est utilisé pour définir les valeurs initiales dans la collection, de sorte qu'il s'intègre de cette façon. –

+0

Droite. Le 'new' manquant aurait dû être un drapeau rouge .. mais n'ayant jamais utilisé cette syntaxe, j'ai pris l'opérateur égal à la lettre. –

0

Il échoue avec une exception de référence nulle car vous avez déclaré une variable (Dictionary) non-initialisée, donc null.

Lorsque vous tentez d'ajouter les entrées à l'aide de la syntaxe d'initialisation, vous essayez d'écrire des données dans un objet nul. Lorsque vous remplacez la ligne par un "= new Dictionary ...", vous créez un nouvel objet à référencer dans le dictionnaire, ce qui vous permet d'y ajouter des entrées.

(Dans l'exemple de Jon Skeet, la collection Controls doit déjà avoir été créé par le formulaire, par conséquent, il fonctionne bien)

+0

Oui, bien sûr. Ma question était: pourquoi autoriser cette syntaxe? –

+0

Assez juste.Jon a répondu à votre question, donc j'ai pensé que je remplirais la question au cas où vous ne comprendriez pas l'échec. –

4

Vous pouvez toujours utiliser la syntaxe que vous voulez dans un constructeur:

Dictionary<string, string> dictionary = new Dictionary<string, string> 
      { 
       {"a", "b"}, 
       {"c", "d"} 
      };