C#4.0扩展了通用类型和接口的协同和逆向.一些界面(如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的接受和返回值.
想象一下,如果它是共变的.那么你可以写这个(对于明显的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中的唯一值应为null或引用香蕉或子类的实例.
现在,为什么即使在逻辑上,类也不能是协变的…我认为在实现层面上引入了问题,在编程层面上也是非常严格的限制.例如,考虑一下:
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 – C#4中的委托和接口方差,刚刚引入了公开功能的语法.我不相信CLR曾经支持泛型类差异.