Насколько корректна эта инициализация словаря в C#?

Я наткнулся на следующее, и мне интересно, почему это не вызвало ошибку синтаксиса.

var dict = new Dictionary<string, object>
{
    ["Id"] = Guid.NewGuid(),
    ["Племена"] = new List<int> { 4, 5 },
    ["MyA"] = new Dictionary<string, object>
    {
        ["Имя"] = "Соло",
        ["Очки"] = 88
    }
    ["OtherAs"] = new List<Dictionary<string, object>>
    {
        new Dictionary<string, object>
        {
            ["Точки"] = 1999 г.
        }
    }
};

Обратите внимание, что "," отсутствует между "MyA" и "OtherAs".

Именно здесь и происходит путаница:

  1. Код компилируется.
  2. Итоговый словарь "dict" содержит только три элемента: "Id", "Tribes" и "MyA".
  3. Значения для всех элементов, кроме "MyA", верны,
  4. "MyA" принимает объявленное значение для "OtherAs", а его исходное значение игнорируется.

Почему это не является незаконным? Так задумано?

Комментарии к вопросу (9)
Решение

Пропущенная запятая делает всю разницу. Она заставляет индексатор ["OtherAs"] быть примененным к этому словарю:

new Dictionary
{
    ["Name"] = "Solo",
    ["Points"] = 88
}

То есть, по сути, вы говорите:

new Dictionary
{
    ["Name"] = "Solo",
    ["Points"] = 88
}["OtherAs"] = new List
{
    new Dictionary
    {
        ["Points"] = 1999
    }
};

Обратите внимание, что это выражение присваивания (x = y). Здесь x - это словарь с "Именем" и "Точками", индексированный с помощью "OtherAs", а y - это List. Выражение присваивания оценивается в присваиваемое значение (y), которое является списком словарей.

Результат всего выражения присваивается ключу "MyA", поэтому "MyA" имеет список словарей.

Вы можете убедиться, что все происходит именно так, изменив тип словаря x:

new Dictionary
{
    [1] = "Solo",
    [2] = 88
}
// compiler error saying "can't convert string to int"
// so indeed this indexer is applied to the previous dictionary
["OtherAs"] = new List
{
    new Dictionary
    {
        ["Points"] = 1999
    }
}

Вот ваш код, но переформатированный и с добавлением некоторых скобок, чтобы проиллюстрировать, как компилятор разобрал его:

["MyA"] 
= 
(
    (
        new Dictionary
        {
            ["Name"] = "Solo",
            ["Points"] = 88
        }["OtherAs"] 
    )
    = 
    (
        new List
        {
            new Dictionary
            {
                ["Points"] = 1999
            }
        }
    )
)
Комментарии (2)

Здесь происходит то, что вы создаете словарь, а затем индексируете его. Затем возвращается результат выражения индексации/присвоения, который и присваивается в слот словаря MyA.

Это:

["MyA"] = new Dictionary 
{
   ["Name"] = "Solo",
   ["Points"] = "88" 
}
["OtherAs"] = new List
{
   new Dictionary
   {
       ["Points"] = 1999
   }
}

Можно разделить на следующие псевдокоды:

var temp = new Dictionary
{ 
   ["Name"] = "Solo", 
   ["Points"] = 88 
};
// indexed contains result of assignment
var indexed = temp["OtherAs"] = new List
{
   new Dictionary
   {
      ["Points"] = 1999
   }
};
// value is set to result of assignment from previous step
["MyA"] = indexed;
// temp is discarded

Возвращается результат присвоения индексатору второго словаря (присвоение возвращает присвоенное значение/правую часть) Этот словарь является временным локальным, который просто "исчезает в эфире". Результат работы индексатора (список словарей) - это то, что в итоге помещается в основной словарь.

Это странный случай, в который легче попасть из-за использования object в качестве типа значений словаря.

Комментарии (0)