C# 4.0 进一步扩展了通用类型和接口(interface)的协变和逆变。一些接口(interface)(如 IEnumerable<T>
)是协变的,所以我可以这样做:
IEnumerable<object> ie = new List<string>();
但是这条线呢?我得到一个编译时错误
List<Object> list = new List<String>();
//Cannot implicitly convert type List<string>' to List<object>'
我的意思是,如果 List<T>
实现 IEnumerable<T>
为什么 List<T>
还是不变的?是否有一个很好的反例来解释为什么在 C# 中不允许这样做?
最佳答案
首先,类在 C# 中始终不变。你不能像这样声明一个类:
// Invalid
public class Foo<out T>
其次 - 对于您给出的示例更重要的是 - List<T>
无法在 T
中声明为协变或逆变无论如何,因为它有成员接受和返回 T
类型的值.
想象一下,如果它是协变的。然后你可以这样写(对于明显的 Fruit
类层次结构):
List<Banana> bunchOfBananas = new List<Banana>();
// This would be valid if List<T> were covariant in T
List<Fruit> fruitBowl = bunchOfBananas;
fruitBowl.Add(new Apple());
Banana banana = bunchOfBananas[0];
您希望最后一行做什么?从根本上说,你不应该能够添加 Apple
引用实际执行时类型为 List<Banana>
的对象.如果你把一个苹果加到一串香蕉上,它就会掉下来。相信我,我已经尝试过了。
最后一行应该在类型方面是安全的 - List<Banana>
中的唯一值应该是 null
或对 Banana
实例的引用或子类。
现在至于为什么类不能是协变的,即使它们可以逻辑上是......我相信这会在实现级别引入问题,而且非常 在编程级别也有限制。例如,考虑一下:
public class Foo<out T> // Imagine if this were valid
{
private T value;
public T Value { get { return value; } }
public Foo(T value)
{
this.value = value;
}
}
那可能仍然是无效的——变量仍然是可写的,这意味着它算作一个“输入”槽。您必须使每个变量类型为 T
只读...这只是初学者。我强烈怀疑会有更深层次的问题。
就纯粹的实用主义而言,CLR 已支持 v2 中的委托(delegate)和接口(interface)变体 - C# 4 刚刚引入了语法来公开该功能。我不相信 CLR 曾经支持泛型类变体。
关于c# - 为什么实现变体接口(interface)的类保持不变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13107071/