C# 泛型中Func的协方差和逆变换
我需要更多关于泛型和委托差异的信息。以下代码段未编译: 错误CS1961无效差异:类型参数“TIn”必须为 在“Test.F(Func)”上协变有效锡是 逆变的 为什么编译器抱怨C# 泛型中Func的协方差和逆变换,c#,generics,variance,C#,Generics,Variance,我需要更多关于泛型和委托差异的信息。以下代码段未编译: 错误CS1961无效差异:类型参数“TIn”必须为 在“Test.F(Func)”上协变有效锡是 逆变的 为什么编译器抱怨TIn是逆变的和TOut-协变的,而Func却期望完全相同的方差 编辑 对我来说,主要的限制是我希望我的测试接口将TOut作为协变的,以便使用它,如下所示: public Test<SomeClass, ISomeInterface> GetSomething () { return new Test
TIn
是逆变的和TOut
-协变的,而Func却期望完全相同的方差
编辑
对我来说,主要的限制是我希望我的测试接口将TOut作为协变的,以便使用它,如下所示:
public Test<SomeClass, ISomeInterface> GetSomething ()
{
return new TestClass<SomeClass, AnotherClass> ();
}
公共测试GetSomething()
{
返回新的TestClass();
}
假定公共类另一个类:ISomeInterface
从接口定义中删除in和out-关键字:
public interface Test<TIn, TOut>{
TOut F (Func<TIn, TOut> transform);
}
公共接口测试{
TOut F(Func变换);
}
删除输入和输出关键字:
public interface Test<TIn, TOut>
{
TOut F (Func<TIn, TOut> transform);
}
公共接口测试
{
TOut F(Func变换);
}
您可以在此处了解它们的含义:
如果类型仅用作方法参数的类型,而不用作方法返回类型,则可以在泛型接口或委托中声明为逆变类型
type参数仅用作接口方法的返回类型,而不用作方法参数的类型。Variance是指能够用比最初声明的派生类型更多或更少的派生类型替换类型参数。例如,
IEnumerable
对于T
是协变的,这意味着如果您从对IEnumerable
对象的引用开始,您可以将该引用分配给类型为IEnumerable
的变量,其中V
可从U
分配(例如U
继承V
)。这是可行的,因为任何试图使用IEnumerable
的代码都希望只接收V
的值,并且由于V
可从U
赋值,因此只接收U
的值也是有效的
对于协变参数,如T
,您必须指定一个类型,其中目标类型与T
相同,或可从T
指定。对于逆变参数,它必须走另一条路。目标类型必须与类型参数相同或可分配给类型参数
那么,您试图编写的代码在这方面是如何工作的呢
当您声明Test
时,您承诺将该接口的实例Test
分配给任何具有类型Test
的目的地是有效的,其中U
可以分配给TIn
,而TOut
可以分配给V
(当然,它们是相同的)
同时,让我们考虑一下您的<代码>转换< /COD>委托是什么。
Func
类型差异要求,如果要将该值分配给其他值,它也满足差异规则。也就是说,目标Func
必须具有可从T
分配的U
,以及可从V
分配的TResult
。这可以确保期望接收值U
的委托目标方法将获得其中一个值,并且接收该值的代码可以接受该方法返回的值(类型为V
)
重要的是,您的接口方法F()接口声明承诺TOut
将仅用作接口成员的输出。但是通过使用transform
委托,方法F()
将接收一个值TOut
,并将该值输入该方法。同样,允许方法F()
将TIn
的值传递给transform
委托,使其成为接口实现的输出,即使您承诺TIn
仅用作输入
换句话说,调用的每一层都颠倒了变化的意义。接口中的成员必须仅将协变类型参数用作输出,将逆变参数用作输入。但当在传递给接口成员或从接口成员返回的委托类型中使用这些参数时,它们在某种意义上会发生反转,并且必须遵守这方面的差异
一个具体的例子:
假设我们有一个接口的实现,Test
。如果编译器允许您的声明,则允许您将该实现的值Test
分配给类型为Test
的变量。也就是说,最初的实现承诺允许任何类型为对象
的对象作为输入,并且只返回类型为字符串
的值。声明为Test
的代码可以安全地使用它,因为它将string
对象传递给需要对象
值的实现(string
是对象
),它将从返回string
值的实现中接收类型为object
的值(同样,string
是对象,因此也是安全的)
但您的接口实现要求代码传递类型为Func
的委托。如果允许您(如上所述)将接口实现视为测试
,则使用重传实现的代码将能够将Func
的委托传递给方法F()
。实现中的方法F()
允许将类型为object
的任何值传递给委托,但该类型为Func
的委托只希望传递类型为字符串的值。如果F()
传递了其他内容,例如仅传递了一个普通的旧新对象()
,
public interface Test<TIn, TOut>{
TOut F (Func<TIn, TOut> transform);
}
public interface Test<TIn, TOut>
{
TOut F (Func<TIn, TOut> transform);
}
public delegate R F<in T, out R> (T arg);
public interface I<in A, out B>{
B M(F<A, B> f);
}
class C : I<Mammal, Mammal>
{
public Mammal M(F<Mammal, Mammal> f) {
return f(new Giraffe());
}
}
I<Tiger, Animal> i = new C();
Func<Tiger, Animal> f = (Tiger t) => new Lizard();
i.M(f);
public delegate R F<T, R> (T arg);