Como esta inicialização do dicionário C # está correta?

64

Eu me deparei com o seguinte e estou me perguntando por que não gerou um erro de sintaxe.

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
        }
    }
};

Observe que "," está ausente entre "MyA" e "OtherAs".

É aqui que a confusão acontece:

  1. O código é compilado.
  2. O dicionário final "dict" contém apenas três elementos: "Id", "Tribes" e "MyA".
  3. O valor para todos, exceto "MyA", está correto,
  4. "MyA" aceita o valor declarado para "OtherAs", enquanto seu valor original é ignorado.

Por que isso não é ilegal? Isso é intencional?

Dantte
fonte
11
@ Steve é ​​sobre isso que ele está perguntando. Eu colei isso no sharplab e parece que inicialmente OtherAsé adicionado como uma chave para o que seria o MyAdicionário. (Nome = Solo, Pontos = 88 mais OtherAs = Lista <Dicionário <string, objeto >>), exceto que nunca o atribui . Em vez disso, coloca a lista de ditados, agora contendo apenas a entrada Points = 1999 no MyAslot, substituindo o que se pensaria pertencer a esse local.
pinkfloydx33
11
O link foi muito longo para colar no primeiro comentário. Isso é realmente estranho. sharplab.io/...
pinkfloydx33
11
Sim, eu testei com o LinqPad e obtive o mesmo resultado. Não tenho certeza do que está acontecendo aqui. Vamos ver se algum guru de C # poderia lançar alguma luz aqui.
Steve
11
Se você alterar o primeiro dicionário para <string, string> and modify Points to "88", você receberá um erro do compilador "Não é possível converter implicitamente o tipo 'System.Collections.Generic.List <System.Collections.Generic.Dictionary <string, object >>' para 'string'" o que realmente me ajudou a descobrir a resposta!
pinkfloydx33
2
@ OlegI Eu não acho que é um bug do compilador. Muitas boas explicações já foram fornecidas nas respostas. Mas se você tentar var test = new Dictionary<string, object> { ["Name"] = "Solo", ["Points"] = 88 }["OtherAs"] = new List<Dictionary<string, object>> { new Dictionary<string, object> { ["Points"] = 1999 } };, explicará melhor.
MKR

Respostas:

54

A vírgula ausente faz toda a diferença. Faz com que o indexador ["OtherAs"]seja aplicado neste dicionário:

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

Então, basicamente, você está dizendo:

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

Observe que esta é uma expressão de atribuição ( x = y). Aqui xestá o dicionário com "Nome" e "Pontos", indexados com "OtherAs"e yé o List<Dictionary<string, object>>. Uma expressão de atribuição é avaliada para o valor que está sendo atribuído ( y), que é a lista de dicionários.

O resultado dessa expressão inteira é atribuído à chave "MyA", e é por isso que "MyA" tem a lista de dicionários.

Você pode confirmar que é isso que está acontecendo alterando o tipo do dicionário 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
    }
}

Aqui está o seu código, mas reformatado e alguns parênteses adicionados para ilustrar como o compilador o analisou:

["MyA"] 
= 
(
    (
        new Dictionary<string, object>
        {
            ["Name"] = "Solo",
            ["Points"] = 88
        }["OtherAs"] 
    )
    = 
    (
        new List<Dictionary<string, object>>
        {
            new Dictionary<string, object>
            {
                ["Points"] = 1999
            }
        }
    )
)
Vassoura
fonte
11
Foi alterando o tipo de valor do dicionário de objectpara o stringque me deu uma mensagem de erro semelhante e o momento aha que deu a resposta. Parece que você me venceu - eu culpo digitando a resposta no meu telefone :-p
pinkfloydx33
19

O que está acontecendo aqui é que você está criando um dicionário e indexando-o. O resultado da expressão indexador / atribuição é retornado e é isso que está sendo atribuído ao MyAslot do dicionário.

Este:

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

Pode ser dividido no seguinte código psuedo:

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

O resultado da atribuição ao indexador do segundo dicionário é retornado (a atribuição retorna o valor atribuído / lado direito). Esse dicionário é um local temporário que apenas "desaparece no éter". O resultado do indexador (a lista de dicionários) é o que é colocado no dicionário principal no final.

Este é um caso estranho, que é mais fácil de se encaixar devido ao uso objectcomo valores do tipo do dicionário.

pinkfloydx33
fonte