Hoe initialisatie van inline-array-werk zoals b.v. Woordenboekinitialisatie?

Why is it possible to initialize a Dictionary like this:

var dict = new Dictionary() { 
    { "key1", 1 },
    { "key2", 2 }
};

...but not to initialize, say, an array of KeyValuePair objects in exactly the same way:

var kvps = new KeyValuePair[] {
    { "key1", 1 },
    { "key2", 2 }
};
// compiler error: "Array initializers can only be used in a variable 
// or field initializer.  Try using a new expression instead."

I realize that I could make the second example work by just writing new KeyValuePair() { "key1", 1 }, etc for each item. But I'm wondering if it's possible to use the same type of concise syntax that is possible in the first example.

Als het niet mogelijk is, wat maakt het type Woordenboek dan zo speciaal?

20
@pst ja, ik denk dat je dichter bij de kern komt van wat ik vraag. Ik ben niet erg goed in het schrijven van vragen ...
toegevoegd de auteur McGarnagle, de bron
Ik hou van waar deze vraag naartoe gaat, maar het lijkt ongeraffineerd. Ik vind het een goed idee om te focussen op Lijst versus Woordenboek (om de Array uit de weg te ruimen), hoewel het foutbericht anders kan zijn? (Musing: Hoe weet iemand hoe een KVP te maken van {a, b} ? ;-)
toegevoegd de auteur user166390, de bron
(Ik denk echter niet dat ik de titel helemaal goed heb gekregen.)
toegevoegd de auteur user166390, de bron

6 antwoord

De syntaxis van de collectie-initialisatie wordt vertaald in aanroepen van Toevoegen met het juiste aantal parameters:

var dict = new Dictionary();
dict.Add("key1", 1);
dict.Add("key2", 2);

Deze speciale initializersyntaxis werkt ook in andere klassen met een Toevoegen -methode en implementeert IEnumerable . Laten we een volledig gekke klas maken alleen om te bewijzen dat er niets speciaals is aan Dictionary en dat deze syntaxis voor elke geschikte klasse kan werken:

// Don't do this in production code!
class CrazyAdd : IEnumerable
{
    public void Add(int x, int y, int z)
    {
        Console.WriteLine(x + y + z);//Well it *does* add...
    }

    public IEnumerator GetEnumerator() { throw new NotImplementedException(); }
}

Nu kun je dit schrijven:

var crazyAdd = new CrazyAdd
{
    {1, 2, 3},
    {4, 5, 6}
};

uitgangen:

6
15

See it working online: ideone

Wat betreft de andere soorten waar je om vroeg:

  • It doesn't work on an array because it has no Add method.
  • List has an Add method but it has only one parameter.
33
toegevoegd
Ok ... maar Lijst heeft een add-methode, en proberen die dezelfde fout produceert.
toegevoegd de auteur McGarnagle, de bron
Dat is een zeer slechte Add (). Ik vind het leuk.
toegevoegd de auteur lesderid, de bron
heb dit voor altijd afgevraagd, bedankt
toegevoegd de auteur Dave Cousineau, de bron
Zie update voor uitleg.
toegevoegd de auteur Mark Byers, de bron

Het werkt wel met het Woordenboek, omdat het een overbelasting heeft voor Toevoegen waarvoor twee argumenten nodig zijn. Arrays hebben zelfs geen Add -methode, laat staan ​​één met twee argumenten.

The Dictionary class is specially designed to work with KeyValuePair<,> internally, that is the only reason you do not need the call the constructor manually, instead the two-argument Add is called and constructs the KeyValuePair under the hood.

Every other IEnumerable> does not have this special implementation and therefore has to be initialized this way:

var array = new KeyValuePair[] {
    new KeyValuePair(1, 2),
    new KeyValuePair(3, 4)
};

Je kunt hetzelfde gedrag creëren met je eigen klassen, zoals laten we zeggen dat je dit implementeert:

class ThreeTupleList : List>
{
    public void Add(T1 a, T2 b, T3 c)
    {
        this.Add(new Tuple(a, b, c));
    }

   //You can even implement a two-argument Add and mix initializers
    public void Add(T1 a, T2 b)
    {
        this.Add(new Tuple(a, b, default(T3)));
    }
}

je kunt het zo initialiseren en zelfs drie-, twee- en een-argument-initializers mixen:

var mylist = new ThreeTupleList()
{
    { 1, "foo", 2.3 },
    { 4, "bar", 5.6 },
    { 7, "no double here" },
    null
};
11
toegevoegd

Uw probleem komt voort uit het feit dat het een array is, geen verzameling.

var kvps = new KeyValuePair[] {
    { "key1", 1 },
    { "key2", 2 }
};

zou eigenlijk moeten zijn:

var kvps = new KeyValuePair[] {
    new KeyValuePair("key1", 1),
    new KeyValuePair("key2", 2)
};

De giveaway is de haakjes. [] is een array. {} is een verzameling.

3
toegevoegd
Misschien was mijn vraag niet duidelijk uitgedrukt, maar lees hem alstublieft van dichterbij. Ik noemde uw voorbeeld in mijn antwoord. Ik weet hoe ik de initialisatie moet schrijven, maar ik vraag me af wat de onderliggende reden is dat de eenvoudigere syntaxis niet mogelijk is.
toegevoegd de auteur McGarnagle, de bron

Dank aan meerdere antwoordapparaten voor het erop wijzen dat de Add -methode de magische zaak geheime saus is die de initialisatiesyntaxis doet werken. Dus ik kon mijn doel bereiken door de betreffende klasse te erven (KeyValuePair):

public class InitializableKVPs : IEnumerable>
{
    public void Add(T1 key, T2 value) 
    {
        throw new NotImplementedException();
    }

    public IEnumerator>  GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator  IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

Dit wordt nu geaccepteerd door de compiler:

var kvps = new InitializableKVPs {
    { "key1", 1 },
    { "key2", 2 }
};

Edit: Philip Daubmeier's answer has an actual, concise implementation of this.

2
toegevoegd
Precies. Zie mijn bijgewerkte antwoord voor een zeer eenvoudige implementatie hiervan, als je gewoon erven van een reeds geïmplementeerde ICollection <> , bijvoorbeeld List <>
toegevoegd de auteur Philip Daubmeier, de bron
Net ontdekt dat je zelfs initializer-elementen kunt mixen met een verschillend aantal argumenten :) Heb mijn antwoord nog een keer bijgewerkt ...
toegevoegd de auteur Philip Daubmeier, de bron

Negeer IDictionary voor de syntaxis van C# 6.0

In het geval dat iemand hier komt, zoals ik deed, en een aantal toetsaanslagen voor de nieuwe syntaxis van de C# initializer van de woordenboeken van de # 6 wil opslaan, kan dit worden gedaan, maar moet in plaats daarvan worden afgeleid van IDictionary. Je hoeft deze [] set-methode alleen maar te implementeren om dit te laten werken, waardoor er een hoop niet-geïmplementeerde methoden overblijven.

De klasse-implementatie ziet er als volgt uit:

// Seriously!  Don't do this in production code! Ever!!!
public class CrazyAdd2 : IDictionary
{
    public int this[string key]
    {
        get { throw new NotImplementedException(); }
        set {Console.WriteLine($"([{key}]={value})"); }
    }

#region NotImplemented
// lots of empty methods go here
#endregion
}

dan om het te gebruiken:

var crazyAdd2 = new CrazyAdd2
{
    ["one"] = 1,
    ["two"] = 2,
};

en de output:

([one]=1)
([two]=2)

En hier is een viool die het hele ding demonstreert:

https://dotnetfiddle.net/Lovy7m

1
toegevoegd

Je ziet het misschien niet, maar het is dezelfde syntaxis. Het enige verschil is dat een array elementen als invoer neemt, waarbij een woordenboek een tweedimensionale matrix gebruikt.

int[] a = new int[]{5,6};
int[,] a = new int[]{{5,6},{3,6}};
Dictionary a = new Dictionary{{5,6},{3,6}};
0
toegevoegd
Dat is niet precies de reden waarom deze syntactische suiker mogelijk is. Eigenlijk is dit te wijten aan de implementatie van het generieke woordenboek, dat een twee-argument Toevoegen levert waarmee de KeyValuePair voor u onder de motorkap wordt geconstrueerd.
toegevoegd de auteur Philip Daubmeier, de bron