Wie ist diese C # -Wörterbuchinitialisierung korrekt?

64

Ich bin auf Folgendes gestoßen und frage mich, warum es keinen Syntaxfehler ausgelöst hat.

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

Beachten Sie, dass das "," zwischen "MyA" und "OtherAs" fehlt.

Hier passiert die Verwirrung:

  1. Der Code wird kompiliert.
  2. Das endgültige Wörterbuch "dict" enthält nur drei Elemente: "Id", "Tribes" und "MyA".
  3. Der Wert für alle außer "MyA" ist korrekt,
  4. "MyA" nimmt den deklarierten Wert für "OtherAs" an, während der ursprüngliche Wert ignoriert wird.

Warum ist das nicht illegal? Ist das beabsichtigt?

Dantte
quelle
1
@ Steve, danach fragt er. Ich habe dies in sharplab eingefügt und es sieht so aus, OtherAsals würde es zunächst als Schlüssel für das MyAWörterbuch hinzugefügt . (Name = Solo, Punkte = 88 plus OtherAs = Liste <Wörterbuch <Zeichenfolge, Objekt >>), außer dass es dann nie zugewiesen wird . Stattdessen wird die Liste der Diktate platziert, die jetzt nur noch den Eintrag "Einzelpunkte = 1999" in den MyASlot enthält und überschreibt, was man für dort hingehört.
pinkfloydx33
1
Der Link war zu lang, um den ersten Kommentar einzufügen. Das ist in der Tat seltsam. sharplab.io/…
pinkfloydx33
1
Ja, ich habe es mit LinqPad getestet und das gleiche Ergebnis erzielt. Ich bin mir nicht sicher, was hier los ist. Mal sehen, ob ein C # -Guru hier etwas Licht ins Dunkel bringen könnte.
Steve
1
Wenn Sie das erste Wörterbuch in ändern <string, string> and modify Points to "88" , wird ein Compilerfehler angezeigt. "Der Typ 'System.Collections.Generic.List <System.Collections.Generic.Dictionary <Zeichenfolge, Objekt >>' kann nicht implizit in 'Zeichenfolge' konvertiert werden." was mir tatsächlich geholfen hat, die Antwort herauszufinden!
pinkfloydx33
2
@OlegI Ich glaube nicht, dass es ein Compiler-Fehler ist. Viele gute Erklärungen bereits in Antworten gegeben. Aber wenn Sie versuchen, var test = new Dictionary<string, object> { ["Name"] = "Solo", ["Points"] = 88 }["OtherAs"] = new List<Dictionary<string, object>> { new Dictionary<string, object> { ["Points"] = 1999 } };wird es weiter erklären.
MKR

Antworten:

54

Das fehlende Komma macht den Unterschied. Dadurch wird der Indexer ["OtherAs"]auf dieses Wörterbuch angewendet:

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

Sie sagen also im Wesentlichen:

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

Beachten Sie, dass dies ein Zuweisungsausdruck ( x = y) ist. Hier xist ein Wörterbuch mit "Name" und "Punkte", indiziert mit "OtherAs"und yist dasList<Dictionary<string, object>> . Ein Zuweisungsausdruck ergibt den zugewiesenen Wert ( y), bei dem es sich um die Liste der Wörterbücher handelt.

Das Ergebnis dieses gesamten Ausdrucks wird dann dem Schlüssel "MyA" zugewiesen, weshalb "MyA" die Liste der Wörterbücher enthält.

Sie können bestätigen, dass dies der Fall ist, indem Sie den Typ des Wörterbuchs ändern 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
    }
}

Hier ist Ihr Code, aber neu formatiert und einige Klammern hinzugefügt, um zu veranschaulichen, wie der Compiler ihn analysiert hat:

["MyA"] 
= 
(
    (
        new Dictionary<string, object>
        {
            ["Name"] = "Solo",
            ["Points"] = 88
        }["OtherAs"] 
    )
    = 
    (
        new List<Dictionary<string, object>>
        {
            new Dictionary<string, object>
            {
                ["Points"] = 1999
            }
        }
    )
)
Kehrmaschine
quelle
1
Das Ändern des Wertetyps des Wörterbuchs von objectin stringgab mir eine ähnliche Fehlermeldung und den Aha- Moment, der die Antwort auslöste. Scheint, als hättest du mich geschlagen - ich beschuldige die Eingabe der Antwort auf meinem Handy :-p
pinkfloydx33
19

Was hier passiert, ist, dass Sie ein Wörterbuch erstellen und dann darin indizieren. Das Ergebnis des Indexer- / Zuweisungsausdrucks wird dann zurückgegeben, und genau das wird dem zugewiesenMyA Wörterbuchsteckplatz .

Diese:

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

Kann in folgenden Pseudocode aufgeteilt werden:

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

Das Ergebnis der Zuweisung an den Indexer des zweiten Wörterbuchs wird zurückgegeben (die Zuweisung gibt den zugewiesenen Wert / rechte Seite zurück). Dieses Wörterbuch ist ein temporäres lokales Wörterbuch, das nur "im Äther verschwindet". Das Ergebnis des Indexers (der Liste der Wörterbücher) wird am Ende in das Hauptwörterbuch eingefügt.

Dies ist ein seltsamer Fall, der aufgrund der Verwendung objectals Typ der Wörterbuchwerte leichter zu erfassen ist.

pinkfloydx33
quelle