这是集合初始化器的一个特例。
在 C# 中,数组初始值设定项花括号已被泛化为可与任何集合类构造函数一起使用。
如果任何类实现了System.Collections.IEnumerable 并且具有一个或多个Add() 方法,则它都支持这些。 Eric Lippert has a good post about this type of "pattern matching" in C#:编译器在这里所做的是他们所谓的“duck typing”,而不是传统的强类型 OOP,其中类的能力是基于继承和接口实现来识别的。 C# 在几个地方这样做。那篇文章中有很多我不知道的东西。
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}");
}
}
然后编译:
var f = new Foo { 0, 1, 2, "Zanzibar", { DateTime.Now, 3.7 } };
这是语法糖:
var f = new Foo();
f.Add(0);
f.Add(1);
f.Add(2)
f.Add("Zanzibar");
f.Add(DateTime.Now, 3.7);
你可以用这些玩一些非常奇怪的游戏。我不知道全力以赴是否是个好主意(实际上我确实知道——它不是),但你可以。我编写了一个命令行解析器类,您可以在其中通过集合初始化程序定义选项。它有十几个 Add 的重载,带有不同的参数列表,其中许多是通用的。编译器可以推断的任何东西都是公平的游戏。
同样,您可以将其从收益递减推到滥用功能的地步。
您看到的是相同初始化器语法的扩展,它允许您为类本身已经创建的不可分配成员执行集合初始化器:
public class Bar
{
public Foo Foo { get; } = new Foo();
}
现在……
var b = new Bar { Foo = { 0, "Beringia" } };
{ 0, "Beringia" } 是 Foo 实例的集合初始化器,Bar 为自己创建;这是语法糖:
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
当您以这种方式看待它时,编译器愿意在语法糖初始化程序用法中解决Foo.Add() 的重载是有道理的。我认为能够做到这一点很棒,但我对他们选择的语法并不是 100% 满意。如果您发现赋值运算符是一个红鲱鱼,其他人也会。
但我不是语法仲裁者,这可能是所有相关人员的最佳选择。
最后,这也适用于对象初始化器:
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" };
}
所以...
var b = new Bar { Foo = { 0, "Beringia" }, Baz = { Name = "Arbitrary" } };
实际上变成了……
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
b.Baz.Name = "Arbitrary";
我们无法初始化Bar.Baz,因为它没有setter,但我们可以初始化它的属性,就像我们可以初始化Foo 中的项目一样。即使它们已经被附加到实际构造函数的不同对象初始化程序初始化,也是如此。
如您所料,集合初始化器是累积的:Bar.Foo 将包含三个项目:{ "1000", "0", "Beringia" }。
当您将花括号视为一列赋值语句或 Add() 重载调用的简写时,这一切都会成为焦点。
但我同意在没有实际分配左值的情况下,等号是不和谐的。
奖金
这是我了解到的另一个模式匹配功能from that Eric Lippert article:
public static class HoldMyBeerAndWatchThis
{
public static IEnumerable<int> Select(Func<String, String> f)
{
yield return f("foo").Length;
}
}
所以……
var x = from s in HoldMyBeerAndWatchThis select s;
select 工作所需要的只是你选择的东西必须有一个名为 Select 的方法,它返回像IEnumerable 中所述的东西嘎嘎声 EricLippert 在the linked article 中对foreach 的评论(感谢Eric!),并采用Func<T,T> 参数。