欢迎光临散文网 会员登陆 & 注册

第 91 讲:C# 3 之集合初始化器

2022-03-01 11:06 作者:SunnieShine  | 我要投稿

下面我们来看第二种初始化器语法:集合初始化器。

对于数组来说,数组的初始化器对数组对象的初始化来说相当方便。但是很遗憾的是,也就数组有初始化器。对于 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 方法。混在一起一来是造成可读性降低,二来是给编译器分析代码上增添额外的复杂性。

第 91 讲:C# 3 之集合初始化器的评论 (共 条)

分享到微博请遵守国家法律