提问人:wjmolina 提问时间:7/13/2013 最后编辑:Mike Perrenoudwjmolina 更新时间:2/1/2023 访问量:5106
C# 数组的协方差和逆方差 [duplicate]
Covariance and Contravariance with C# Arrays [duplicate]
问:
在维基百科上阅读一篇关于协方差和逆方差的文章时,我遇到了以下粗体句子:
首先考虑数组类型构造函数:从类型中我们可以创建类型(“动物数组”)。我们是否应该将其视为
Animal
Animal[]
- 协变:a 是
Cat[]
Animal[]
- 逆变:a 是 a
Animal[]
Cat[]
- 还是两者都不是(不变的)?
如果我们希望避免类型错误,并且数组同时支持读取和写入元素,那么只有第三种选择是安全的。显然,并不是每个都可以被视为 ,因为从数组中读取的客户端会期望一个 Cat,但 可能包含例如 .因此,逆变规则是不安全的。
Animal[]
Cat[]
Animal[]
Dog
相反,
猫[]
不能被视为动物[]。
应该总是可以把 a 放入 .对于协变数组,这不能保证是安全的,因为后备存储实际上可能是一个猫数组。因此,协变规则也不安全——数组构造函数应该是不变的。请注意,这只是可变数组的问题;协变规则对于不可变(只读)数组是安全的。Dog
Animal[]
我理解这个概念;我只想举个例子来说明这在 C# 中是如何“不能保证安全的”。
答:
这在编译时并不安全。换句话说,有些代码在语言规则下是合法的,但在执行时失败了,没有任何显式的强制转换来给出“这可能会失败”的大警告信号。CLR 确保在执行时只有有效的写入才能成功。例如:
string[] strings = new string[1];
object[] objects = strings;
objects[0] = new object();
这将在执行时引发异常 (ArrayTypeMismatchException
)。另一种选择是在执行时允许它,此时将是对非字符串对象的引用,这显然是不好的。strings[0]
另请参阅最近的博客文章:
- 我用一个通用包装器来介绍性能和安全性
- 一个来自 BCL 团队的不可变数组
- Eric Lippert 关于方差的博客系列的第 2 部分(该系列主要针对泛型,但这部分是关于数组的)
评论
The CLR makes sure it's safe at execution time.
SetValue
我认为他们想说的是:
Dog dog = new Dog();
Cat[] cats = new Cat[] { catOne, catTwo, catThree };
Animal[] animals = cats;
animals.Add(dog);
此代码的第 3 行是不合法的,因为您应该始终能够执行第 4 行(将 a 添加到数组 s)。但是,如果第 3 行是合法的,那么第 4 行就不合法了(因为您不能将 a 添加到数组 s)。Dog
Animal
Dog
Cat
评论