C# 数组的协方差和逆方差 [duplicate]

Covariance and Contravariance with C# Arrays [duplicate]

提问人:wjmolina 提问时间:7/13/2013 最后编辑:Mike Perrenoudwjmolina 更新时间:2/1/2023 访问量:5106

问:

在维基百科上阅读一篇关于协方差和逆方差的文章,我遇到了以下粗体句子:

首先考虑数组类型构造函数:从类型中我们可以创建类型(“动物数组”)。我们是否应该将其视为AnimalAnimal[]

  • 协变:a 是Cat[]Animal[]
  • 逆变:a 是 aAnimal[]Cat[]
  • 还是两者都不是(不变的)?

如果我们希望避免类型错误,并且数组同时支持读取和写入元素,那么只有第三种选择是安全的。显然,并不是每个都可以被视为 ,因为从数组中读取的客户端会期望一个 Cat,但 可能包含例如 .因此,逆变规则是不安全的。Animal[]Cat[]Animal[]Dog

相反,猫[]不能被视为动物[]。应该总是可以把 a 放入 .对于协变数组,这不能保证是安全的,因为后备存储实际上可能是一个猫数组。因此,协变规则也不安全——数组构造函数应该是不变的。请注意,这只是可变数组的问题;协变规则对于不可变(只读)数组是安全的。DogAnimal[]

我理解这个概念;我只想举个例子来说明这在 C# 中是如何“不能保证安全的”。

C# .NET OOP 协方差逆

评论

0赞 SLaks 7/13/2013
blogs.msdn.com/b/ericlippert/archive/2007/10/17/......
1赞 nawfal 5/29/2014
我不知道为什么它被关闭为链接问题的副本。一个更相关的问题:为什么是数组协方差被认为是如此可怕

答:

23赞 Jon Skeet 7/13/2013 #1

这在编译时并不安全。换句话说,有些代码在语言规则下是合法的,但在执行时失败了,没有任何显式的强制转换来给出“这可能会失败”的大警告信号。CLR 确保在执行时只有有效的写入才能成功。例如:

string[] strings = new string[1];
object[] objects = strings;
objects[0] = new object();

这将在执行时引发异常 (ArrayTypeMismatchException)。另一种选择是在执行时允许它,此时将是对非字符串对象的引用,这显然是不好的。strings[0]

另请参阅最近的博客文章:

评论

0赞 Mike Perrenoud 7/13/2013
当你说,你指的是它抛出异常的事实吗?这就是确保它在执行时安全的方法吗?The CLR makes sure it's safe at execution time.
0赞 Jon Skeet 7/13/2013
@MichaelPerrenoud:是的,没错。我会编辑以澄清这一点。
0赞 Mike Perrenoud 7/13/2013
太棒了,谢谢,我只是想确保我明白了!
1赞 Sriram Sakthivel 7/13/2013
@JonSkeet我测试了您的代码,它会抛出 ArrayTypeMismatchException。但是我放置了一个断点并通过 DebuggerVisualiser 手动设置 objects[0] = new object(),它不会抛出任何错误并且它可以工作。稍后检查字符串[0]。GetType() 返回 System.Object
0赞 Sriram Sakthivel 7/14/2013
@Johnbot你能详细说明一下吗?调试器如何在数组中设置值?如果它使用方法,它将抛出异常SetValue
1赞 Tim 7/13/2013 #2

我认为他们想说的是:

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)。DogAnimalDogCat

评论

2赞 Hutch 10/31/2015
您描述了 C# 团队可以实现此目的的一种方式 - 但实际上,第 3 行确实编译,并且第 4 行在运行时被捕获(从技术上讲,第 4 行不会编译,因为数组---没有 Add 方法,但 animals[0] = dog 会在运行时捕获。