第 89 讲:C# 3 之隐式数组变量类型的初始化器
今天来看一个跟上一回不一样又有点类似的新语法。
Part 1 引例
其中第二个的 new int[4]
的 4
可以不写,即 new int[] { ... }
。但是有一个问题是,int
会被重复书写一次。new int[4]
的 int
实际上是可以不写的。因为在初始化器里,我们已经可以清晰地获取和了解到每一个元素的类型,1、2、3、4 都是 int
的字面量,那么 new T[]
的 T
就应该是 int
。
可是,早期的语法并未对这个语法进行简化,C# 3 才开始意识到问题,于是得到了优化。
Part 2 语法
C# 3 允许我们省略带有初始化器的 new T[]
的 T
。注意这个说法,是必须带有初始化器的。因为我们必须要从数组的初始化器辨识和了解到 T
的具体类型。如果初始化器不存在的话,我们不论如何都没办法获得到 T
的实际结果。
可以对比一下就可以发现,new[]
是一个全新的语法,它用于隐式初始化一个数组类型。
而且,这样的语法可以拓展到多维的矩形数组里。如果数组是二维数组甚至是三维数组的话,都可以使用这个语法来初始化:
但是请注意。这里需要有一个限制。因为我们初始化器会给出全部的元素,因此我们完全可以通过初始化器推断得到 new T[]
的中括号语法里的数字是多少。比如这个二维数组我们知道,它等于 new int[3, 3]
。
但是,因为简化语法的关系,我们既然省略了数据类型在 new
的旁边,那么自然而然也会需要省略掉这个没必要写出来的数字。因此,如果你写的是这样的语法:
它就不正确了。数字是没必要写出来的,所以你给出了数字还省略类型,C# 3 不会允许你这么做,因为没必要写出来还写出来,写出来还容易出错。因此,这个语法不支持在中括号里显式地给出数字。
综上所述,我们来做个总结。下面四种写法,可以对照注释看看哪些可以哪些不行。
而对于变量已经定义,但需要重新赋值的时候:
作为折衷的语法方案,new[] { ... }
的语法相对于 { ... }
仅初始化器语法要长一些,而比 new T[] { ... }
的语法来说又要短一点。但 { ... }
语法不能用于重新赋值,因此也不够灵活。new[] { ... }
语法在初始化和重新赋值的时候都可以使用,因此还是很方便的。
Part 3 隐式数组类型的语法不依赖接收方
可以从代码里看到,我们之所以要求省略类型的时候必须要有初始化器,是需要看初始化器的元素是什么类型,才能断言和断定具体的类型的。因此,它根本不依赖于接收方。
换句话说,我们才学到了 var
,那么我们下面这样的语法也是可以的:
你说说,var
是什么,而 new[]
省略的类型又是什么?是不是 var
表示 int[]
,而 new[]
省略的是 int
啊?因为一个单纯的元素就可以确定类型了,所以这样的语法也可以。
再来看一个:
请问,每一个 new[]
都省略了什么类型,而 var
又表示什么?
答案是这样的:
你答对了吗?它是一个锯齿数组,即使用两个 string[]
类型为元素构成的一维数组。注意,最外层的 new[]
这里省略的是 string[]
而不是 string
。
Part 4 隐式锯齿数组类型的元素一致性
考虑下面这个例子:
new[]
那么问题来了,还有一个 new[]
,它应该是什么类型呢?int
和 string
是毫不相关的两个类型,根本无法找到合适的类型匹配。因此,C# 3 的这个语法要求,对于锯齿数组来说,省略类型的时候必须要完全确定实际上的类型,才能省略。如果两个和多个类型无法找到相同的类型匹配项,就会产生错误。
有人会问,new[]
可以是 new object[]
啊,毕竟 object
派生了 int
和 string
。但实际上,C# 3 并不支持这个匹配规则,隐式类型省略省略的类型,必须要求匹配项的元素全部类型都得一致。注意我这里说的是“一致”,也就意味着是相同类型才是 OK 的,而即使是兼容的类型(比如刚才说的 object
这样的匹配逻辑)也是不行的。
例如这个复杂的赋值是错误的语法,需要让其可以匹配,必须改成这样: