与C#数组的协方差和逆差
在阅读维基百科关于协方差和逆变的文章的一部分时,我遇到了以下粗体句:
首先考虑数组类型构造函数:从类型
Animal
我们可以创建类型Animal[]
(“动物数组”)。 我们应该把它视为
- 协变:
Cat[]
是Animal[]
- 逆变:
Animal[]
是Cat[]
- 或两者(不变)?
如果我们希望避免类型错误,并且数组支持读取和写入元素,那么只有第三种选择是安全的。 显然,并非每个
Animal[]
都可以被视为Cat[]
,因为从数组中读取的客户端将期望Cat,但Animal[]
可能包含例如Dog
。 所以逆变规则并不安全。相反,
Cat[]
不能被视为Animal[]
。 应该总是可以将Dog
放入Animal[]
。 对于协变数组,这不能保证是安全的,因为后备存储实际上可能是一组猫。 所以协变规则也不安全 – 数组构造函数应该是不变的。 请注意,这只是可变数组的一个问题; 协变规则对于不可变(只读)数组是安全的。
我理解这个概念; 我只是想要一个如何在C#中“无法保证安全”的例子 。
它在编译时不安全。 换句话说,这些代码在语言规则中是合法的,但是在执行时失败,没有任何明确的强制转换来给出“这可能失败”的大警告信号。 CLR确保在执行时只有有效写入成功。 例如:
string[] strings = new string[1]; object[] objects = strings; objects[0] = new object();
这将在执行时抛出exception( ArrayTypeMismatchException
)。 替代方案是在执行时允许它,此时strings[0]
将是对非字符串对象的引用,这显然是坏的。
另见最近的博客文章:
- 一个是关于使用通用包装器的性能和安全性的
- 一个来自BCL团队的不可变数组
- Eric Lippert一般关于方差的博客系列的第2部分 (该系列主要针对generics,但这部分是在数组上)
我想他们想说的是:
Dog dog = new Dog(); Cat[] cats = new Cat[] { catOne, catTwo, catThree }; Animal[] animals = cats; animals.Add(dog);
此代码的第3行不合法,因为您应该始终能够执行第4行(将Dog
添加到Animal
的数组中)。 但是如果第3行是合法的,那么第4行就不合法(因为你不能将Dog
添加到Cat
的数组中)。