【问题标题】:Covariance and Contravariance with C# Arrays [duplicate]C#数组的协变和逆变[重复]
【发布时间】:2013-07-11 05:53:37
【问题描述】:

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

首先考虑数组类型构造函数:从类型Animal 我们可以创建类型Animal[](“动物数组”)。我们应该把它当作

  • 协变:Cat[]Animal[]
  • 逆变:Animal[]Cat[]
  • 或者两者都不(不变)?

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

相反,Cat[] 不能被视为Animal[]应该始终可以将Dog 放入Animal[]。对于协变数组,这不能保证是安全的,因为后备存储实际上可能是一个猫数组。所以协变规则也不安全——数组构造函数应该是不变的。请注意,这只是可变数组的问题;协变规则对于不可变(只读)数组是安全的。

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

【问题讨论】:

标签: c# .net oop covariance contravariance


【解决方案1】:

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

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

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

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

【讨论】:

  • 当您说The CLR makes sure it's safe at execution time. 时,您指的是它抛出异常的事实吗?这就是确保它在执行时安全的方法吗?
  • @MichaelPerrenoud:是的,完全正确。我将进行编辑以澄清这一点。
  • 太棒了,谢谢,我只是想确保我理解了!
  • @JonSkeet 我测试了它抛出 ArrayTypeMismatchException 的代码。但是我放置了一个断点并通过 DebuggerVisualiser 手动设置 objects[0] = new object() 它不会引发任何错误并且可以正常工作。稍后检查 strings[0].GetType() 返回 System.Object
  • @Johnbot 你能详细说明一下吗?调试器如何在数组中设置值?如果它使用SetValue 方法会抛出异常
【解决方案2】:

我认为他们想说的是:

Dog dog = new Dog();
Cat[] cats = new Cat[] { catOne, catTwo, catThree };
Animal[] animals = cats;
animals.Add(dog);

此代码的第 3 行不合法,因为您应该始终能够执行第 4 行(将Dog 添加到Animals 的数组中)。但是如果第 3 行是合法的,那么第 4 行就不合法(因为你不能将 Dog 添加到 Cats 的数组中)。

【讨论】:

  • 您描述了 C# 团队可以实现的一种方式 - 但实际上,第 3 行 确实 编译,第 4 行被捕获运行时(从技术上讲,第 4 行无法编译,因为没有数组的 Add 方法 --- 但 animal[0] = dog 会在运行时被捕获。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-06
  • 2011-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-16
相关资源
最近更新 更多