首先,类在 C# 中总是不变的。你不能像这样声明一个类:
// Invalid
public class Foo<out T>
其次-对于您给出的示例更重要的是-List<T>
无论如何都不能声明为协变或逆变T
,因为它具有接受和返回 type 值的成员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>
. 如果你把一个苹果加到一堆香蕉里,它就会掉下来。相信我,我已经试过了。
最后一行在类型方面应该是安全的 - a 中的唯一值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;
}
}
那仍然可能必须是无效的——变量仍然是可写的,这意味着它被视为一个“in”槽。您必须将每个类型的变量设置为T
只读......这只是为了初学者。我强烈怀疑会有更深层次的问题。
就纯粹的实用主义而言,CLR 已经支持 v2 的委托和接口变化 - C# 4 只是引入了公开该功能的语法。我不相信 CLR 曾经支持泛型类差异。