¿Cómo es correcta esta inicialización del diccionario C #?

64

Me topé con lo siguiente y me pregunto por qué no generó un error de sintaxis.

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 falta "," entre "MyA" y "OtherAs".

Aquí es donde ocurre la confusión:

  1. El código se compila.
  2. El diccionario final "dict" contiene solo tres elementos: "Id", "Tribus" y "MyA".
  3. Los valores para todos excepto "MyA" son correctos,
  4. "MyA" toma el valor declarado para "OtherAs", mientras que su valor original se ignora.

¿Por qué no es esto ilegal? ¿Es esto por diseño?

Dantte
fuente
1
@ Steve, eso es lo que está preguntando. Pegué esto en sharplab y parece que inicialmente OtherAsse agregó como una clave en lo que sería el MyAdiccionario. (Nombre = Solo, Puntos = 88 más Otros Como = Lista <Diccionario <cadena, objeto >>) excepto que nunca lo asigna . En su lugar, coloca la lista de dictados, que ahora solo contiene la única entrada Puntos = 1999 en la MyAranura que anula lo que uno pensaría que pertenece allí.
pinkfloydx33
1
El enlace fue demasiado largo para pegar en el primer comentario. Esto es extraño de hecho. sharplab.io/…
pinkfloydx33
1
Sí, lo probé con LinqPad y obtuve el mismo resultado. No estoy seguro de lo que está pasando aquí. Veamos si algún gurú de C # podría arrojar algo de luz aquí.
Steve
1
Si cambia el primer diccionario a <string, string> and modify Points to "88" `, aparece un error del compilador" No se puede convertir implícitamente el tipo 'System.Collections.Generic.List <System.Collections.Generic.Dictionary <string, object >>' a 'string' " que realmente me ayudó a encontrar la respuesta!
pinkfloydx33
2
@OlegI No creo que sea un error del compilador. Muchas buenas explicaciones ya se proporcionan en las respuestas. Pero si lo intentas var test = new Dictionary<string, object> { ["Name"] = "Solo", ["Points"] = 88 }["OtherAs"] = new List<Dictionary<string, object>> { new Dictionary<string, object> { ["Points"] = 1999 } };te lo explicaré más a fondo.
MKR

Respuestas:

54

La coma que falta hace toda la diferencia. Hace que el indexador ["OtherAs"]se aplique en este diccionario:

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

Entonces esencialmente estás diciendo:

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

Tenga en cuenta que esta es una expresión de asignación ( x = y). Aquí xhay un diccionario con "Nombre" y "Puntos", indexado con "OtherAs"y yes el List<Dictionary<string, object>>. Una expresión de asignación evalúa el valor que se asigna ( y), que es la lista de diccionarios.

El resultado de toda esta expresión se asigna a la clave "MyA", por lo que "MyA" tiene la lista de diccionarios.

Puede confirmar que esto es lo que está sucediendo cambiando el tipo de diccionario 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
    }
}

Aquí está su código, pero reformateado y algunos paréntesis agregados para ilustrar cómo el compilador lo ha analizado:

["MyA"] 
= 
(
    (
        new Dictionary<string, object>
        {
            ["Name"] = "Solo",
            ["Points"] = 88
        }["OtherAs"] 
    )
    = 
    (
        new List<Dictionary<string, object>>
        {
            new Dictionary<string, object>
            {
                ["Points"] = 1999
            }
        }
    )
)
Barrendero
fuente
1
Fue cambiar el tipo de valor del diccionario de objecta stringque me dio un mensaje de error similar y el momento aha que provocó la respuesta. Parece que me ganaste
pinkfloydx33
19

Lo que sucede aquí es que está creando un diccionario y luego lo está indexando. Luego se devuelve el resultado de la expresión de indexación / asignación y eso es lo que se asigna a la MyAranura del diccionario.

Esta:

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

Se puede dividir en el siguiente 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

Se devuelve el resultado de la asignación al indexador del segundo diccionario (la asignación devuelve el valor asignado / lado derecho) Ese diccionario es un local temporal que simplemente "desaparece en el éter". El resultado del indexador (la lista de diccionarios) es lo que se coloca en el diccionario principal al final.

Este es un caso extraño, en el que se hace más fácil caer debido al uso de objectcomo el tipo de valores del diccionario.

pinkfloydx33
fuente