可能重复:
同一类型参数的协变和逆变
out
您可以使用关键字将泛型类型参数声明为协变:
interface ICovariant<out R>
in
您可以使用关键字将泛型类型参数声明为逆变:
interface IContravariant<in R>
您还可以同时支持不同类型的参数:
interface IVariant<out R, in A>
那么为什么你不能同时支持一个类型参数呢?
可能重复:
同一类型参数的协变和逆变
out
您可以使用关键字将泛型类型参数声明为协变:
interface ICovariant<out R>
in
您可以使用关键字将泛型类型参数声明为逆变:
interface IContravariant<in R>
您还可以同时支持不同类型的参数:
interface IVariant<out R, in A>
那么为什么你不能同时支持一个类型参数呢?
那么为什么你不能同时支持一个类型参数呢?
请记住,如果类型参数是输出安全的,则接口只能在类型参数中是协变的,并且如果类型参数是输入安全的,则接口只能在类型参数中是逆变的。
语法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。)
它行不通。考虑一下(如果in out
存在):
public class INewList<in out T>
{
public T DoIt(T item);
}
这是不可能满足的,因为人们期望 anINewList<T>
将与更窄和更宽类型的接口兼容。
考虑INewList<Feline>
:
如果 in/out 都可能,则此接口将等效于,INewList<Animal>
但这对于 in 位置无效,因为它允许您扩大到类型参数:
... DoIt(Animal item)
这是行不通的,因为这意味着您可以new Dog()
从原始界面传入一个预期猫科动物的实例。
同样在出局位置上反向,因为它允许:
Puma DoIt(...)
这将是无效的,因为原始界面可以传回任何猫科动物,不一定是 Puma。