第 91 讲:C# 3 之集合初始化器
下面我们来看第二种初始化器语法:集合初始化器。
对于数组来说,数组的初始化器对数组对象的初始化来说相当方便。但是很遗憾的是,也就数组有初始化器。对于 List<>
类型来说,它们也需要初始化,但 List<>
这些集合类型并没有合适的初始化器语法,因此非常不方便,你只能使用 Add
方法挨个添加元素。
好在,C# 的集合类型,添加元素全都叫 Add
,这促使了 C# 语言的语法设计团队定义出了一种新的语法。
Part 1 数组初始化器的推广
C# 3 开始,允许各种各样的集合使用类似数组初始化器的语法来添加、追加元素到集合里。就拿常见的 List<>
类型来说,原本的语法是这样的:
C# 3 允许这么写了:
我们把类似数组初始化器的语法放在集合的初始化操作之中的 { 1, 3, 6, 10 }
这个东西叫做集合初始化器(Collection Initializer)。
Part 2 集合初始化器的使用条件
我们从前面的规则来看,集合初始化器都既然叫集合初始化器了,显然这个语法适用于一个集合。可问题在于,从严谨的角度来说,啥样的东西才能叫集合呢?这种比较意识流的概念肯定是不能放在 C# 的语法设计里去的。
这里就得重新提及鸭子类型的概念了。在之前我们介绍过鸭子类型的基本概念,以及第一种鸭子类型的语法:foreach
循环的满足条件。要想一个数据类型的实例可以使用 foreach
循环,必须要求对象要么实现 IEnumerable
或者 IEnumerable<>
接口,要么自己实现相关的迭代器模式,即定义一个类型,实现 bool
返回值的 MoveNext
方法和返回实例的 Current
属性。
今天我们要学习的集合初始化器是第二种鸭子类型语法。它需要满足一个条件,才能允许使用。
先说结论:集合初始化器需要这个数据类型实现 IEnumerable
接口或者从它派生下来接口,并包含一个不返回任何数值的 Add
方法,传入一个参数表示存到集合里去。
意思应该比较清晰了吧。我们回到 List<>
类型,发现 List<>
在 C# 3 上天生就支持这个集合初始化器的语法,就是因为它满足我们刚才说的这两个条件。首先,List<>
集合里包含 Add
方法,void
返回值,并传入 T
类型的参数,所以满足了第二个条件;并且这个集合也实现了 IEnumerable<T>
接口。这个接口是从非泛型版本的 IEnumerable
接口派生的,因此它算是满足了第一个条件。正是因为这个对象满足了两个条件,因此它可以认定为“可以使用集合初始化器的语法”。
注意这个鸭子类型的语法。这次的语法必须要求实现接口,但 foreach
满足条件里不一定需要实现接口,而是只需要有迭代器模式的代码就行,所以一定要区分开。
Part 3 其它细节
3-1 允许在末尾多一个额外的逗号
这个语法和对象初始化器差不太多,也可以省略无参构造器的小括号,也允许集合初始化器最后一个元素后面的额外的逗号。
3-2 不要混用集合初始化器和对象初始化器
虽然这样的语法很好用,但很遗憾的是,C# 并不允许混用对象初始化器和集合初始化器。下面的语法就不可以:
Add