Несогласованность компиляции инициализатора словаря С#

Следующий код компилируется, но завершается ошибкой с NullReferenceException:

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

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

Если вы замените строку с пометкой «сбой» на следующую, она работает (как и ожидалось):

Dictionary = new Dictionary<string, string> 

Есть ли какая-то цель в неудачном синтаксисе - можно ли его успешно использовать в каком-то другом случае? Или это недоработка компилятора?


person Ben M    schedule 23.09.2009    source источник


Ответы (3)


Нет, это не ошибка... это недостаток вашего понимания синтаксиса инициализации :)

Идея

Dictionary = { ... }

предназначен для случаев, когда вызывающая сторона имеет доступ чтение к свойству коллекции, но не имеет доступа запись. Другими словами, такие ситуации:

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

В основном это заканчивается вызовами Add, но без предварительного создания новой коллекции. Итак, этот код:

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

эквивалентно:

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

Хорошим примером того, где это полезно, является коллекция Controls для пользовательского интерфейса. Ты можешь это сделать:

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

но на самом деле вы не могли установить свойство Controls, потому что оно доступно только для чтения.

person Jon Skeet    schedule 23.09.2009
comment
Так что он используется для добавления элементов в словарь, созданный конструктором — я должен был это понять. Но это странное использование оператора равенства, так как эффект заключается в добавлении того, что уже есть в словаре (конструктор мог сначала добавить элементы). - person Ben M; 23.09.2009
comment
Вроде как да... но в то же время он используется для установки начальных значений в коллекции, так что он подходит таким образом. - person Jon Skeet; 23.09.2009
comment
Верно. Отсутствующий new должен был быть красным флажком... но, никогда не используя этот синтаксис, я воспринял оператор равенства слишком буквально. - person Ben M; 23.09.2009

Вы все еще можете использовать синтаксис, который вы хотите в конструкторе:

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

Он завершается с ошибкой с нулевой ссылкой, потому что вы объявили переменную (словарь), которая не инициализирована, следовательно, она равна нулю.

Когда вы пытаетесь добавить в него записи, используя синтаксис инициализатора, вы пытаетесь записать данные в нулевой объект.

Когда вы заменяете строку на "= new Dictionary...", вы создаете новый объект, на который Dictionary будет ссылаться, и, следовательно, вы сможете успешно добавлять в него записи.

(В примере Джона Скита коллекция Controls должна быть уже создана формой, поэтому она работает нормально)

person Jason Williams    schedule 23.09.2009
comment
Справедливо. Джон ответил на ваш вопрос, поэтому я подумал, что укажу причину, если вы не понимаете сбоя. - person Jason Williams; 24.09.2009