如何理解泛型委托使用中的contravariance(逆变)特性
核心原因:两种赋值对应完全不同的C#语法规则
你观察到的差异,本质上是两类完全独立的转换规则的区别,你之前的认知只适用于其中一类场景:
- 第一类赋值:
genericDerivedFunc = genericBaseFunc
这是两个已构造的泛型委托类型实例之间的转换,即FuncDelegate<BaseClass>类型到FuncDelegate<DerivedClass>类型的转换。泛型类型之间的协变/逆变转换,确实必须要求泛型参数用in/out显式声明可变性,没有in关键字时,FuncDelegate<BaseClass>和FuncDelegate<DerivedClass>是完全无关的两种类型,自然不允许隐式转换,这和你的认知完全一致。 - 第二类赋值:
FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;
这不是委托类型之间的转换,而是方法组到委托的隐式转换,这类转换的规则只校验方法签名和委托签名是否兼容,和泛型委托的可变性声明没有任何关系。
方法组到委托的签名兼容规则
只要满足以下条件,方法就可以隐式转换为对应委托:
- 方法返回值类型和委托返回值类型兼容
- 委托定义的每一个参数,都可以安全传入方法的对应参数位置
你遇到的场景中,委托要求传入DerivedClass类型参数,而方法接收的是BaseClass类型参数:调用委托时你只会传入DerivedClass的实例,DerivedClass是BaseClass的子类,自然可以安全传给方法的BaseClass参数,因此签名兼容,转换合法。
这个特性和泛型、逆变完全无关,哪怕用非泛型委托也可以复现:
// 非泛型委托,要求入参为DerivedClass delegate bool NonGenericFunc(DerivedClass dc); // 直接赋值接收BaseClass的方法,完全合法 NonGenericFunc f = BaseFunc;
认知补正
你之前认为的「必须要有in关键字才能支持这类参数方向的转换」的结论,仅适用于两个构造泛型委托类型之间的实例转换,不适用于方法直接到委托的赋值场景。
内容的提问来源于stack exchange,提问作者AdmiralOrange
相关产品推荐
相关产品推荐

