C# · 12月 29, 2021

c# – 为什么实现变体接口的类保持不变?

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#中的类总是不变的.你不能这样声明一个类: // Invalidpublic class Foo<out T>

其次 – 更重要的是你给出的例子 – List< T>无论如何,它们都不能被声明为协变或逆向,因为它拥有类型T的接受和返回值.

想象一下,如果它是共变的.那么你可以写这个(对于明显的Fruit类层次结构):

List<Banana> bunchOfBananas = new List<Banana>();// This would be valid if List<T> were covariant in TList<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曾经支持泛型类差异.