为什么实现变体接口的类保持不变? [英] Why classes that implement variant interfaces remain invariant?

查看:23
本文介绍了为什么实现变体接口的类保持不变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆