为什么实现变体接口的类保持不变? [英] Why classes that implement variant interfaces remain invariant?
问题描述
C# 4.0 进一步扩展了泛型类型和接口的协变和逆变.一些接口(如 IEnumerable
)是协变的,所以我可以做这样的事情:
C# 4.0 has extended the co and contravariance further for generic types and interfaces. Some interfaces (like IEnumerable<T>
) are covariants, so I can do things like:
IEnumerable<object> ie = new List<string>();
但是这条线呢?我有一个编译时错误
but what about this line? I got a compile-time error
List<Object> list = new List<String>();
//Cannot implicitly convert type List<string>' to List<object>'
我的意思是,如果 List
实现 IEnumerable
为什么 List
仍然是不变的?有没有一个很好的反例来解释为什么在 C# 中不允许这样做?
I mean, if List<T>
implement IEnumerable<T>
why List<T>
is still invariant? Is out there a good counterexample that explain why this should not be allowed in C#?
推荐答案
首先,类在 C# 中总是不变.你不能像这样声明一个类:
Firstly, classes are always invariant in C#. You can't declare a class like this:
// Invalid
public class Foo<out T>
其次 - 对于您给出的示例,更重要的是 - List
无论如何都不能在 T
中声明为协变或逆变,因为它具有接受和返回 T
类型值的成员.
Secondly - and more importantly for the example you've given - List<T>
couldn't be declared to be covariant or contravariant in T
anyway, as it has members both accepting and returning values of type T
.
想象一下它是否是协变的.然后你可以写这个(对于明显的 Fruit
类层次结构):
Imagine if it were covariant. Then you could write this (for the obvious Fruit
class hierarchy):
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];
你希望最后一行做什么?从根本上说,您不应该向实际执行时类型为 List
的对象添加 Apple
引用.如果你在一堆香蕉中加入一个苹果,它就会掉下来.相信我,我已经尝试过了.
What would you expect that last line to do? Fundamentally, you shouldn't be able to add an Apple
reference to an object whose actual execution-time type is List<Banana>
. If you add an apple to a bunch of bananas, it falls off. Believe me, I've tried.
最后一行应该就类型而言是安全的 - List
中的唯一值应该是 null
或对Banana
或子类的实例.
The last line should be safe in terms of types - the only values within a List<Banana>
should be null
or references to instances of Banana
or a subclass.
现在至于为什么即使类在逻辑上可以是协变的,为什么类不能协变......我相信这会在实现级别引入问题,并且也会非常 在编程层面也有限制.例如,考虑这个:
Now as for why classes can't be covariant even when they could logically be... I believe that introduces problems at the implementation level, and would also be very restrictive at the programming level as well. For example, consider this:
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
类型的每个变量设置为只读……而这仅适用于初学者.我强烈怀疑会有更深层次的问题.
That would still probably have to be invalid - the variable is still writable, meaning it counts as an "in" slot. You'd have to make every variable of type T
read-only... and that's just for starters. I strongly suspect that there would be deeper problems.
就纯粹的实用主义而言,CLR 已支持 v2 中的委托和接口变化 - C# 4 刚刚引入了用于公开该功能的语法.我不相信 CLR 曾经支持泛型类变化.
In terms of pure pragmatism, the CLR has supported delegate and interface variance from v2 - C# 4 just introduced the syntax to expose the feature. I don't believe the CLR has ever supported generic class variance.
这篇关于为什么实现变体接口的类保持不变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!