乔恩·斯基特在他的著作“深度的C#”中试图回答以下问题:
为什么我不能将
List<string>转换为List<object>?
为了解释这一点,他从一个代码片段开始,其中包括以下两行:
Animal[] animals = new Cat[5]; //Ok. Compiles fine!
List<Animal> animals = new List<Cat>(); //Compilation error!正如注释所述,第一个是编译精细,第二个是编译错误。我不太明白原因。Jon解释了这一点,只说第一个是因为在.NET中数组是协变的,而第二个是不编译的,因为泛型不是协变的(相反,它们是不变的)。而且,数组在.NET中是协变的,因为数组在Java中是协变的,而.NET使其类似于Java。
我对这个简短的答案并不完全满意。我想更详细地了解它,了解编译器是如何处理这种差异的,以及它是如何生成IL和all的。
此外,如果我写(摘自这本书本身):
Animal[] animals = new Cat[5]; //Ok. Compiles fine!
animals.Add(new Turtle()); //this too compiles fine!它编译得很好,但在运行时失败了。如果它必须在运行时失败(这意味着我写的东西没有意义),那么为什么它首先要编译呢?我是否可以在代码中使用实例animals,并且运行时也没有错误?
发布于 2011-09-28 19:06:11
数组在.NET中有一个奇怪的变化历史,在CLR的2.0版本和C# 4.0中添加了对方差的适当支持。然而,数组始终具有协变行为。
Eric在博客帖子中详细介绍了这一点。
有趣的一点是:
从C# 1.0开始,元素类型是引用类型的数组是协变量的。这是完全合法的: Animal[]动物=新Giraffe10; 由于长颈鹿比动物小,并且“make a array”是类型上的协变操作,所以Giraffe[]比Animal[]小,所以实例适合这个变量。 不幸的是,这种特殊的协方差被打破了。它被添加到CLR中,因为Java需要它,而CLR设计人员希望能够支持类似Java的语言。然后,我们将其添加到C#中,因为它位于CLR中。这个决定当时很有争议,我对此不太高兴,但我们现在无能为力。
我自己加的重点。
发布于 2011-09-28 19:54:11
如果它必须在运行时失败,那么为什么它首先要编译?
这正是数组协方差被打破的原因。事实上,我们允许它意味着我们允许在编译时被捕获的错误被忽略,而在运行时被捕获。
我对这个简短的答案并不完全满意。我想更详细地了解它,并深入了解编译器如何处理这种差异.
编译器非常容易地处理这种差异。编译器有大量的代码来确定一种类型何时与另一种类型兼容。该代码的一部分处理数组到数组的转换。该代码的一部分处理泛型到泛型转换。代码是规范相关行的直接转换。
..。以及它是如何产生IL和所有的。
没有必要为协变数组转换生成任何IL。为什么我们需要为两个兼容引用的类型之间的转换生成IL?--这就像问我们为将字符串转换为对象所生成的IL是什么。字符串已经是一个对象,因此没有生成任何代码。
发布于 2011-09-28 19:16:12
我认为consider解释得相当好,但是如果您需要"Ahah“这个时刻,请考虑泛型是如何工作的。
在大多数情况下,像List<>这样的泛型类在外部被视为一个普通类。例如,当您说
List<string>()时,编译器表示ListString()(它包含字符串),编译器不能聪明地通过转换其内部集合的项将ListString转换为ListObject。
从阅读MSDN博客文章可以看出,在使用委托和接口时,.NET 4.0支持协方差和反向方差。它还在第二句中提到Eric的文章。
https://stackoverflow.com/questions/7588183
复制相似问题