与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的数组中)。