使用泛型委托时如何理解逆变?



我正在学习"逆变泛型委托"。

我的理解是:

"in" 关键字指定类型参数是逆变的。
这允许对委托类型进行隐式转换。

如果没有 "in" 关键字,我们不知道类型参数是否是逆变的.
则不允许对委托类型进行隐式转换。

这是我的代码:

public class Test
{
//public delegate bool FuncDelegate<T>(T t);
public delegate bool FuncDelegate<in T>(T t);
public class BaseClass
{
public int x;
}
public class DerivedClass: BaseClass
{
public int y;
}
static bool BaseFunc(BaseClass bc)
{
if (bc.x > 1)
return false;
else
return true;
}
static bool DerivedFunc(DerivedClass dc)
{
if (dc.y > 1)
return false;
else
return true;
}
public static void Main()
{
FuncDelegate<DerivedClass> genericDerivedFunc = DerivedFunc;
FuncDelegate<BaseClass> genericBaseFunc = BaseFunc;
genericDerivedFunc = genericBaseFunc;
FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;
}
}

我的问题

/*
This line is valid when declared as: public delegate bool FuncDelegate<in T>(T t);
This line is invalid when declared as: public delegate bool FuncDelegate<T>(T t);
*/
genericDerivedFunc = genericBaseFunc;

这句话与我的底线一致。

/*
This line is always valid.
*/
FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;

我不明白这行:

"bool BaseFunc(BaseClass bc)"可以隐式转换为bool "FuncDelegate(DerivedClass t)"。

我认为它必须具有"in"关键字来指定逆变。

但是转换可以在没有"in"关键字的情况下完成。

请注意这两个作业的右侧之间的区别:

genericDerivedFunc = genericBaseFunc;
genericDerivedFunc2 = BaseFunc;

第一行的右侧是委托,因此您要将一个委托类型转换为另一个委托类型。这需要差异转换,如 C# 规范中的可用转换中所列:

隐式引用转换包括:

  • 从任何reference_type到接口或委托类型 T(如果它具有隐式标识或引用转换到接口或委托类型 T0 且 T0 可方差转换为 T)。

方差转换需要ins 和outs。

但是,在第二行中,右侧是方法组(方法的名称),因此在第二行中,您实际上是在执行方法组转换。要使此类转换可用,BaseFunc需要与目标委托类型兼容。请注意,这是对方法的要求,而不是对委托类型的要求。要"兼容"。

值得注意的是,方法M与委托类型D"兼容"的两个要求是:

  • 对于每个值参数,存在从D中的参数类型到M中的相应参数类型的标识转换或隐式引用转换。
  • 存在从返回类型M到返回类型D的标识或隐式引用转换。

这些要求使委托类型看起来好像在其所有参数上都具有in修饰符,而在其返回类型上具有out修饰符。

基本上,由于 RHS 是非常不同类型的东西,因此适用不同的规则。

文档在代表部分中对此进行了差异介绍:

.NET Framework 3.5 引入了差异支持,用于在 C# 中的所有委托中将方法签名与委托类型匹配。这意味着,您不仅可以将具有匹配签名的方法分配给委托,还可以将返回更多派生类型(协方差)或接受派生类型(逆变)少于委托类型指定的参数的方法分配给委托。这包括通用委托和非泛型委托。

因此,在您的情况下,这直接属于"接受派生类型(逆变)少于委托类型指定的参数"部分。

如果你看到sharplab的反编译,你会看到FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;实际上被转换为类似的东西:

FuncDelegate<DerivedClass> genericDerivedFunc2 = new FuncDelegate<DerivedClass>(BaseFunc);

genericDerivedFunc = genericBaseFunc;只是将FuncDelegate<BaseClass>简单地分配给FuncDelegate<DerivedClass>FuncDelegate不逆变时,就会失败。

最新更新