Как правильно инициализировать словарь C #?

64

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

var dict = new Dictionary<string, object>
{
    ["Id"] = Guid.NewGuid(),
    ["Tribes"] = new List<int> { 4, 5 },
    ["MyA"] = new Dictionary<string, object>
    {
        ["Name"] = "Solo",
        ["Points"] = 88
    }
    ["OtherAs"] = new List<Dictionary<string, object>>
    {
        new Dictionary<string, object>
        {
            ["Points"] = 1999
        }
    }
};

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

Вот где происходит путаница:

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

Почему это не незаконно? Это по замыслу?

Dantte
источник
1
@ Стив, вот о чем он спрашивает. Я вставил это в sharplab и, похоже , изначально OtherAsдобавляется в качестве ключа в то , что будетMyA словарь. (Name = Solo, Points = 88 плюс OtherAs = List <Dictionary <string, object >>) за исключением того, что оно никогда не назначает его . Вместо этого он помещает список диктов, теперь содержащий только одну запись Points = 1999, в MyAячейку, переопределяющую то, что, как можно подумать, принадлежит ей.
pinkfloydx33
1
Ссылка была слишком длинной для вставки в первый комментарий. Это действительно странно. sharplab.io/…
pinkfloydx33
1
Да, я протестировал его с LinqPad и получил тот же результат. Не уверен, что здесь происходит. Давайте посмотрим, сможет ли какой-нибудь гуру C # пролить свет здесь.
Стив
1
Если вы измените первый словарь на <string, string> and modify Points to «88», то «получите ошибку компилятора». Невозможно неявно преобразовать тип 'System.Collections.Generic.List <System.Collections.Generic.Dictionary <string, object >>' в 'string' " что на самом деле помогло мне понять ответ!
pinkfloydx33
2
@ Олег Я не думаю, что это ошибка компилятора. Многие хорошие объяснения уже приведены в ответах. Но если вы попробуете var test = new Dictionary<string, object> { ["Name"] = "Solo", ["Points"] = 88 }["OtherAs"] = new List<Dictionary<string, object>> { new Dictionary<string, object> { ["Points"] = 1999 } };, объясню это дальше.
MKR

Ответы:

54

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

new Dictionary<string, object>
{
    ["Name"] = "Solo",
    ["Points"] = 88
}

По сути, вы говорите:

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

Обратите внимание, что это выражение присваивания ( x = y). Вот xсловарь с именами и точками, проиндексирован "OtherAs"и yобозначен List<Dictionary<string, object>>. Выражение присваивания оценивается как значение ( y), которое является списком словарей.

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

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

new Dictionary<int, object>
{
    [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<Dictionary<string, object>>
{
    new Dictionary<string, object>
    {
        ["Points"] = 1999
    }
}

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

["MyA"] 
= 
(
    (
        new Dictionary<string, object>
        {
            ["Name"] = "Solo",
            ["Points"] = 88
        }["OtherAs"] 
    )
    = 
    (
        new List<Dictionary<string, object>>
        {
            new Dictionary<string, object>
            {
                ["Points"] = 1999
            }
        }
    )
)
уборщик
источник
1
Она меняется типа значения Словаря от objectк , stringкоторый дал мне подобное сообщение об ошибке и ага момент , который вызвал ответ. Кажется, ты меня побил - я обвиняю, что набрал ответ на своем телефоне :-p
pinkfloydx33
19

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

Эта:

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

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

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

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

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

pinkfloydx33
источник