关于c#:为什么通用接口默认不是co / contravariant?

Why generic interfaces are not co/contravariant by default?

例如IEnumerable接口:

1
2
3
4
public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

在此接口中,泛型类型仅用作接口方法的返回类型,而不用作方法参数的类型,因此它可以是协变的。既然如此,编译器就不能从理论上推断出接口的差异吗?如果可以的话,为什么c要求我们显式地设置co/contravariance关键字。

更新:正如jon skeet提到的,这个问题可以分为以下几个子问题:

  • 编译器能否通过在当前泛型类型及其所有基类型中如何使用泛型类型来推断泛型类型的协变/逆变?

    例如。。.NET Framework 4.0中有多少通用接口参数可以自动标记为co/contravariant而不产生任何歧义?大约70%、80%、90%或100%?

  • 如果可以的话,默认情况下它是否应该对泛型类型应用协变/逆变?至少对于那些它能够从类型使用中分析和推断协变/逆变的类型。


  • 好吧,这里有两个问题。首先,编译器可以一直这样做吗?其次,是否应该(如果可以)?

    对于第一个问题,我将遵从Eric Lippert的意见,他在我在第二版C深度版中提出这一问题时发表了这一评论:

    It's not clear to me that we reasonably could even if we wanted to. We can easily come up
    with situations that require expensive global analysis of all the interfaces in a program
    to work out the variances, and we can easily come up with situations where either it's
    or and no way to decide between them. With both bad
    performance and ambiguous cases it's an unlikely feature.

    (我希望埃里克不介意我一字不差地引用这句话;他以前很擅长分享这些见解,所以我将按过去的方式进行。)

    另一方面,我怀疑还有一些情况下,它是可以推断的,没有歧义,所以第二点仍然是相关的…

    我认为它不应该是自动的,即使编译器可以明确地知道它在一个方面是有效的。尽管在某种程度上扩展一个接口总是一个突破性的变化,但如果只有你一个人实现它,通常情况下就不是这样了。但是,如果人们依赖于您的接口是变量,那么您可能无法在不破坏客户端的情况下向其添加方法…即使他们只是呼叫者,而不是实现者。您添加的方法可能会更改以前协变的接口,使其变为不变量,此时您将中断任何试图协变使用它的调用方。

    基本上,我认为要求这一点明确是可以的——这是一个你应该有意识地做出的设计决策,而不是在没有考虑的情况下意外地以协方差/反方差结束。


    本文解释了编译器无法推断的情况,因此它为您提供了显式语法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface IReadWriteBase<T>
    {
        IReadWrite<T> ReadWrite();
    }

    interface IReadWrite<T> : IReadWriteBase<T>
    {

    }

    你在这里推断inout都起作用吗?