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

Jam*_*man 8 c# generics interface covariance contravariance

可能重复:
同一类型参数的协方差和反演

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

interface ICovariant<out R>
Run Code Online (Sandbox Code Playgroud)

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

interface IContravariant<in R>
Run Code Online (Sandbox Code Playgroud)

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

interface IVariant<out R, in A>
Run Code Online (Sandbox Code Playgroud)

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

jas*_*son 11

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

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

语法out T说这T是一个协变类型参数.

语法in T说这T是一个逆变型参数.

作为T协变类型参数,根据定义输入不安全.

作为T逆变型参数,根据定义输出不安全.

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

因此,T在输入位置T被禁止,并且在输出位置被禁止.

因此,T在界面指定的任何方法上都不能出现在输入位置或任何输出位置.

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

interface IFoo<in and out T> { }
Foo<T> : IFoo<T> { }
Run Code Online (Sandbox Code Playgroud)

然后:

IFoo<Cat> cat = (IFoo<Animal>)new Foo<Dog>();
Run Code Online (Sandbox Code Playgroud)

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

  • 哇,这是一个很难阅读的答案:) (2认同)
  • 实际上,在某些情况下,接口可能包括一个实际上不参与任何方法或属性的类型参数.更大的问题是,如果对于从Object派生的任何T和U,Foo <Object>是Foo <T>的有效替换,而Foo <U>是Foo <Object>的有效替换,那么Foo <U>是Foo <T>的有效替代品.因为协方差和逆变只适用于Object的衍生物,所以说T中的界面是协变的和逆变的,这会使T变得毫无意义. (2认同)

Jam*_*are 5

这行不通。考虑以下因素(如果in out存在):

public class INewList<in out T>
{
    public T DoIt(T item);
}
Run Code Online (Sandbox Code Playgroud)

这将是无法满足的,因为人们期望a INewList<T>与窄类型和宽类型的接口都兼容。

考虑INewList<Feline>

如果输入/输出都可行,则此接口将等效于,INewList<Animal>但对于in位置而言将是无效的,因为它将允许您扩展键入参数:

... DoIt(Animal item)
Run Code Online (Sandbox Code Playgroud)

这行不通,因为那意味着您可以传入new Dog()原始接口中预期有Feline 的实例。

同样,在出局头寸上相反,因为这样可以:

Puma DoIt(...)
Run Code Online (Sandbox Code Playgroud)

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