隐式类型数组
在C# 1和2中, 初始化一个数组作为声明一个变量的一部分其表达式是相当简洁的——但是如果你想在任何地方创建它, 你不得不声明数组的显式类型. 因此下面的例子完全可以听过编译而没有任何问题:
1: string[] names = {"Holly", "Jon", "Tom", "Robin", "William"};
下面的代码则无法工作, 即便我们声明了方法MyMethod的签名为MyMethod(string[] names):
1: MyMethod({"Holly", "Jon", "Tom", "Robin", "William"});
相反, 我们不得不告诉编译器你想要初始化的数组类型:
1: MyMethod(new string[] {"Holly", "Jon", "Tom", "Robin", "William"});
C# 3则允许类似下面这样的调用:
1: MyMethod(new[] {"Holly", "Jon", "Tom", "Robin", "William"});
很明显, 编译器需要去推断出要使用的数组类型. 从大括号里面的元素开始, 如果能够找到一个所有其他元素都可以隐式转换的类型, 那么这就是数组的类型, 否则代码将无法编译. 如果元素是类型无关的的表达式, 例如常量null或者匿名函数, 代码也无法编译. 注意只有表达式的类型会被考虑为整体数组类型的候选. 这意味着我们可能偶尔不得不将一个值显式转换到另外一个范围更小的类型. 例如, 下面的代码将无法编译:
1: new[] { new MemoryStream(), new StringWriter() }
无法从MemoryStream转换到StringWriter, 反之也一样. 虽然两者都能够被转换成object或者IDisposable, 但编译器仅仅考虑表达式本身所产生的类型. 在这个例子中, 如果我们将其中的一个类型显式转换到object或者IDisposable接口, 那么代码将可以编译通过:
1: new[] { (IDisposable) new MemoryStream(), new StringWriter() }
最后整个表达式的类型是IDisposable[]. 比较一下之前我们提到的其他特性, 隐式类型数组有一点点令人泄气. 虽然他们确实让生活变得更加简单一点, 例如传递数组作为参数的例子. 然而你可以争论的一点是其并没有在”用处和复杂度”的平衡上证明自己, 而这一点通常是语言设计者用来决定哪些特性应该成为语言的一部分. 设计人员没有发疯, 然而——有一个很重要的情形, 在那里隐式类型数组是非常关键的. 那就是当你不知道(实际上根本无法知道)数组里面元素的类型名称的时候, 你如何才能进入这种特殊的状态? 待续!