为什么通用接口默认不是共同/逆变?

时间:2010-08-30 19:10:26

标签: c# generics covariance contravariance

例如IEnumerable<T> interface:

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

在此接口中,泛型类型仅用作接口方法的返回类型,不用作方法参数的类型,因此它可以是协变的。给出这个,不能编译从理论上推断出界面的方差吗?如果可以的话,为什么C#要求我们明确设置共同/反向变换关键字。

更新:正如Jon Skeet所提到的,这个问题可以用于子问题:

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

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

  2. 如果可以,应该默认情况下它会对通用类型应用协变/反差?至少对那些能够分析和推断与类型用法相关/变异的类型。

2 个答案:

答案 0 :(得分:16)

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

对于第一个问题,我会推荐Eric Lippert,当我在第二版C#深度中提到这个问题时,他发表了这个评论:

  

我不清楚,即使我们愿意,我们也能合理地做到。我们很容易上来   需要对程序中所有接口进行昂贵的全局分析的情况   找出差异,我们可以轻松地提出任何一种情况   <in T, out U><out T, in U>,无法在他们之间做出决定。两者都不好   性能和模糊的情况这是一个不太可能的功能。

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

另一方面,我怀疑仍然存在无法模糊推断 的情况,所以第二点仍然相关......

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

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

答案 1 :(得分:5)

This article解释说,有些情况下编译器无法推断,因此它为您提供了明确的语法:

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

interface IReadWrite<T> : IReadWriteBase<T>
{

}

您在此处推断inout,这些都有效吗?