Das ist ein Spezialfall eines Auflistungsinitialisierer ist.
In C# wurden die geschweiften Klammern des Array-Initialisierers verallgemeinert, um mit jedem Auflistungsklassenkonstruktor zu arbeiten.
Jede Klasse unterstützt diese, wenn sie System.Collections.IEnumerable
implementiert und eine oder mehrere Methoden Add()
hat. Eric Lippert has a good post about this type of "pattern matching" in C#: Was der Compiler hier macht, ist, was sie "duck typing" nennen, anstatt konventioneller stark typisierter OOP, wo die Fähigkeiten einer Klasse basierend auf Vererbung und Schnittstellenimplementierung erkannt werden. C# macht das an einigen Stellen. Es gibt eine Menge Sachen in diesem Artikel, die ich nicht kannte.
public class Foo : List<String>
{
public void Add(int n)
{
base.Add(n.ToString());
}
public void Add(DateTime dt, double x)
{
base.Add($"{dt.ToShortDateString()} {x}");
}
}
Und dann diese kompiliert:
var f = new Foo { 0, 1, 2, "Zanzibar", { DateTime.Now, 3.7 } };
, die für diese syntaktische Zucker ist:
var f = new Foo();
f.Add(0);
f.Add(1);
f.Add(2)
f.Add("Zanzibar");
f.Add(DateTime.Now, 3.7);
Sie ein paar ziemlich seltsame Spiele mit diesen spielen. Ich weiß nicht, ob es eine gute Idee ist, alles heraus zu gehen (eigentlich weiß ich - es ist nicht), aber Sie können. Ich habe eine Befehlszeilenparser-Klasse geschrieben, in der Sie die Optionen über einen Auflistungsinitialisierer definieren können. Es hat über ein Dutzend Überladungen von Add
mit variierenden Parameterlisten, von denen viele generisch sind. Alles was der Compiler ableiten kann, ist faires Spiel.
Auch hier können Sie diese Push über kehrt zum Punkt der Funktion Missbrauch zu verringern.
Was Sie sehen, ist eine Erweiterung der gleichen initializer Syntax, wo es kann Sie eine Auflistungsinitialisierer für ein nicht übertragbares Element zu tun, die die Klasse selbst bereits erstellt:
public class Bar
{
public Foo Foo { get; } = new Foo();
}
Und jetzt .. .
var b = new Bar { Foo = { 0, "Beringia" } };
{ 0, "Beringia" }
ist eine Sammlung Initialisierer für die Foo
weise, dass für Bar
selbst erzeugt; es ist syntaktischer Zucker für diesen:
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
Die Bereitschaft der Compiler Überlastungen von Foo.Add()
in der syntaktisch-Zucker initializer Nutzung sinnvoll ist, zu lösen, wenn man es so aussehen. Ich denke, es ist großartig, das zu können, aber ich bin mit der von ihnen gewählten Syntax nicht zu 100% zufrieden. Wenn Sie den Zuweisungsoperator als Ablenkungsmanöver gefunden haben, werden es auch andere tun.
Aber ich bin nicht der Syntax Arbiter, und das ist wahrscheinlich am besten für alle Beteiligten.
Schließlich funktioniert das auch mit Objektinitialisierer:
public class Baz
{
public String Name { get; set; }
}
public class Bar
{
public Foo Foo { get; } = new Foo { 1000 };
public Baz Baz { get; } = new Baz { Name = "Initial name" };
}
So ...
var b = new Bar { Foo = { 0, "Beringia" }, Baz = { Name = "Arbitrary" } };
, die in tatsächlich dreht ...
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
b.Baz.Name = "Arbitrary";
Wir können nicht initialisiert werden Bar.Baz
weil es keinen Setter hat, aber wir können seine Eigenschaften initialisieren, genauso wie wir es initialisieren können ems in Foo
. Und das ist auch dann der Fall, wenn sie bereits von einem anderen Objektinitialisierer initialisiert wurden, der an den eigentlichen Konstruktor angehängt wurde.
Auflistungsinitialisierer sind wie erwartet kumulativ: Bar.Foo
wird drei Elemente haben: { "1000", "0", "Beringia" }
.
Wenn Sie an die geschweiften Klammern als Kurzschreibweise für eine Spalte von Zuweisungsanweisungen oder Add()
Überladungsaufrufe denken, wird alles in den Fokus gerückt.
Aber ich stimme zu, dass das Gleichheitszeichen in Fällen, in denen der Lvalue nicht tatsächlich zugewiesen wird, schreit.
Bonus
Hier ist eine andere Pattern-Matching-Funktion, die ich gelernt, über from that Eric Lippert article:
public static class HoldMyBeerAndWatchThis
{
public static IEnumerable<int> Select(Func<String, String> f)
{
yield return f("foo").Length;
}
}
Deshalb ...
var x = from s in HoldMyBeerAndWatchThis select s;
Alles, was Sie für select
arbeiten müssen, ist, dass das, was Sie‘ Die Auswahl von muss eine Methode namens Select
haben, die etwas zurückgibt, das quatscht wieIEnumerable
wie in @ EricLippert Bemerkungen auf foreach
in the linked article (danke Eric!), Und nimmt eine Func<T,T>
Parameter.
Ich habe das gerade neulich entdeckt, ich konnte aber auch noch nicht genau feststellen, wie es funktioniert. – DLeh
Entsprechend der [MSDN-Dokumentation zu Initialisierer] (https://msdn.microsoft.com/en-us/library/bb384062.aspx) scheint es, dass die Kompilierung 'Add' wiederholt aufruft, wenn dieser Initialisierer verwendet wird. Beachten Sie, dass Sie diese Syntax nicht direkt in einer Variablendeklaration verwenden können. z.B. 'IList children = {" childfoo "," childbar "}' kompiliert nicht –
@ stephen.vakil: Manchmal können Sie irgendwie fast. 'string [] children = {" childfoo "," childbar "};' – recursive