19

例如IEnumerable<T>接口:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

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

更新:正如 Jon Skeet 所提到的,这个问题可以归结为子问题:

  1. 编译器可以通过它在当前泛型类型及其所有基本类型中的使用方式来推断泛型类型的协/逆变吗?

    例如..NET Framework 4.0 中有多少通用接口参数可以自动标记为协/逆变而没有任何歧义?大约 70%、80%、90% 或 100%?

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

4

2 回答 2

16

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

对于第一个问题,我将听从 Eric Lippert 的意见,当我在第 2 版 C# in Depth 中准确地提出这个问题时,他发表了这样的评论:

我不清楚即使我们愿意,我们也可以合理地做到这一点。我们可以很容易地想出需要对程序中的所有接口进行昂贵的全局分析以计算出差异的情况,并且我们可以很容易地想出 在它们之间<in T, out U>是否<out T, in U>可以做出决定的情况。由于性能不佳和模棱两可的情况,这是一个不太可能的功能。

(我希望 Eric 不介意我逐字引用此内容;他以前非常善于分享这些见解,所以我将采用过去的形式 :)

另一方面,我怀疑仍有一些情况可以毫无歧义地推断出来,所以第二点仍然是相关的......

我认为它不应该是自动的,即使编译器可以明确地知道它仅以一种方式有效。虽然扩展接口在某种程度上总是一个突破性的变化,但如果你是唯一一个实现它的人,通常不会。但是,如果人们依赖你的接口来变体,你可能无法在破坏客户端的情况下向它添加方法……即使他们只是调用者,而不是实现者。您添加的方法可能会将先前协变的接口更改为不变,此时您会破坏任何试图以协变方式使用它的调用者。

基本上,我认为要求它明确是很好的——这是一个你应该有意识地做出的设计决定,而不是在没有考虑的情况下意外地以协变/逆变结束。

于 2010-08-30T19:14:52.680 回答
5

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

interface IReadWriteBase<T>
{
    IReadWrite<T> ReadWrite();
}

interface IReadWrite<T> : IReadWriteBase<T>
{

}

你在这里推断什么,in或者out,两者都有效?

于 2010-08-30T19:18:37.947 回答