关于c#:用于将对象添加到对象初始化程序中的集合的语法糖

Syntactic sugar for adding items to collections in object initialisers

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()会产生一个Test对象,其中Things集合包含"First""Second"两个对象。

似乎行Things = {"Second" }调用了ThingsAdd方法。如果将ICollection改为IEnumerable,则会出现语法错误,说明"IEnumerable does not contain a definition for 'Add'"。

很明显,在对象初始化器中只能使用这种语法。此类代码无效:

1
2
var test = new Test();
test.Things = {"Test" };

此功能的名称是什么?它是在哪个版本的C中引入的?为什么它只在对象初始化器中可用?


它被称为集合初始值设定项,并被添加到C_3语言规范中(在引言部分的第7.5.10.3节,在当前规范中的第7.6.10.3节)。具体来说,使用的代码使用嵌入的集合初始值设定项。

集合初始值设定项实际上只是调用Add方法,这是根据规范所必需的。

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

0

这就很好地解释了你意想不到的结果。

Why is it only available in object initialisers?

因为其他地方没有意义。您可以自己调用Add方法,而不是为初始化以外的东西使用初始值设定项。


正如Patrick已经提到的,集合初始值设定项顺序调用列表中的Add。这假定您的属性已由构造函数相应地初始化:

1
2
3
4
5
6
7
8
public class MyClass
{
    public List<MyType> TheList { get; private set; }
    public MyClass()
    {
        this.TheList = new List<MyType>();
    }
}

如果没有这样的构造函数初始化您的列表,您将在下面的语句中得到一个NullReferenceException

1
test.Things = {"Test" };

但是,这与以下不同:

2

在这种情况下,您将访问属性setter。如果没有(或者只有我的示例中的private一个),则会导致编译器错误。