支持单个类型参数的协方差和逆变

时间:2011-07-15 15:07:43

标签: c# generics interface covariance contravariance

  

可能重复:
  Covariance and Contravariance on the same type argument

您可以使用out关键字将通用类型参数声明为协变:

interface ICovariant<out R>

您可以使用in关键字将通用类型参数声明为逆变量:

interface IContravariant<in R>

您还可以同时支持不同的类型参数:

interface IVariant<out R, in A>

那么为什么你不能同时支持单一类型参数呢?

2 个答案:

答案 0 :(得分:11)

  

那么为什么你不能同时支持单一类型参数呢?

请记住,如果该类型参数是输出安全的,则接口只能在类型参数中协变,并且如果该类型参数是输入安全的,则接口在类型参数中只能是逆变的。

语法out T表示T是协变类型参数。

语法in T表示T是逆变型参数。

由于T是协变类型参数,因此根据定义输入不安全。

由于T是逆变型参数,因此根据定义输出不安全。

因此,T输入不安全且输出不安全。

因此,输入位置禁止T,输出位置禁止T

因此,T不能出现在接口指定的任何方法的输入位置或任何输出位置。

因此,T根本不能在接口上使用,并且作为类型参数毫无意义。因此,语言设计者禁止你甚至包括在界面上标记为协变和逆变的这种无用类型,以避免丑陋

interface IFoo<in and out T> { }
Foo<T> : IFoo<T> { }

然后:

IFoo<Cat> cat = (IFoo<Animal>)new Foo<Dog>();

(如果您需要阅读输入安全和输出安全,请参阅语言规范的13.1.3.1。)

答案 1 :(得分:4)

这不行。考虑一下(如果in out存在):

public class INewList<in out T>
{
    public T DoIt(T item);
}

这是不可能满足的,因为人们期望INewList<T>将与更窄和更宽类型的接口兼容。

考虑INewList<Feline>

如果输入/输出都可能,则此接口将等同于INewList<Animal>,但这对于位置无效,因为它允许您扩展到类型参数:

... DoIt(Animal item)

哪个不起作用,因为这意味着你可以传入一个new Dog()实例,其中预期从原始界面中有猫。

同样在out位置反向,因为它允许:

Puma DoIt(...)

这将是无效的,因为原始界面可以传回任何猫科动物,不一定是美洲狮。