对象初始化器中添加项的语法糖

7

最近我遇到了一些类似于这样的代码:

public class Test
{
    public ICollection<string> Things { get; set; }

    public Test()
    {
        Things = new List<string> { "First" };
    }

    public static Test Factory()
    {
        return new Test
        {
            Things = { "Second" }
        };
    }
}

调用Test.Factory()会得到一个包含"First""Second"Things集合的Test对象。
看起来,Things = { "Second" }这行代码调用了ThingsAdd方法。如果将ICollection更改为IEnumerable,则会出现语法错误,提示"IEnumerable<string> does not contain a definition for 'Add'"。
另外,很明显只有在对象初始化器中才能使用这种语法。像下面这样的代码是无效的:
var test = new Test();
test.Things = { "Test" };

这个特性叫什么?它在哪个版本的C#中引入的?为什么它只能在对象初始化器中使用?

2
它被称为collection initializer,我相信它比c#6更早,但无法确定确切时间。 - René Vogt
我认为它不是一个集合初始化器,因为那个MSDN页面(以及我找到的其他所有来源)都没有展示语法“Things = { "Second" }”。在每个例子中,第一个 { 都会在前面加上 new 关键字来表示完全新的对象。 - Synoptic
1
它适用于我的VS2010,因此它已经存在于C#3中。将我的语言版本从C#3更改为ISO-2会导致许多编译器错误:“无法使用'集合初始化程序'功能,因为它不是ISO-2 C#语言规范的一部分”,因此它是添加在C#3上的。 - MakePeaceGreatAgain
2
@Synoptic 那么你应该看的是对象初始化器,而不是集合初始化器。 - Servy
2
那么在这种情况下,= 是“添加”而不是“设置”?我不喜欢它 - 非常令人困惑。 - PeteGO
显示剩余5条评论
2个回答

5

它被称为集合初始化器,并在C# 3语言规范(介绍中的第7.5.10.3节,当前规范中的第7.6.10.3节)中添加。具体来说,您使用的代码使用了一个嵌入式集合初始化器。

实际上,集合初始化器只是调用了必须根据规范要求的Add方法。

正如Quantic所评论的那样,规范说:

在等号后指定集合初始化器的成员初始化器是嵌入式集合的初始化。与将新集合分配给字段或属性不同,初始化器中给出的元素将添加到由字段或属性引用的集合中。

这解释了您意外结果的原因。

为什么它只在对象初始化器中可用?

因为在其他地方是没有意义的。你可以直接调用Add方法来代替使用初始值设定器来进行初始化以外的操作。

仅为完整起见,当前规范(版本5.0)要查看的章节号是7.6.10.3。 - Matthew Watson
1
语法 Things = { "Second" } 更具体地说是一个“嵌入式集合初始化器”,如规范中所述:“在等号后指定集合初始化器的成员初始化器是嵌入式集合的初始化。与将新集合分配给字段或属性不同,初始化器中给出的元素将添加到由字段或属性引用的集合中”。我见过的这种语法最接近的例子是在规范的同一部分中,但是针对“嵌套对象初始化器”:P1 = { X = 0, Y = 1 } - Quantic
谢谢@Quantic,这正是我想要的。 - Synoptic

1
正如Patrick所述,集合初始化程序按顺序调用列表上的Add方法。这假定您的属性已根据构造函数进行了初始化:
public class MyClass
{
    public List<MyType> TheList { get; private set; }
    public MyClass() 
    {
        this.TheList = new List<MyType>(); 
    }
}

如果没有这样的构造函数来初始化您的列表,那么在以下语句中您将会得到一个NullReferenceException:
test.Things = { "Test" }; 

然而,这与以下内容不同:
var m = new MyClass { TheList = new List<MyType> { ... } };

如果没有属性的setter(或者只有一个私有的setter,就像我的例子一样),会导致编译错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接