为了简化,假设我有父类和子类:
public class MyParent { }
public class MyChild : MyParent { }
这两个功能与一些代码:
public void DoSomethingBy_MyChild(MyChild myChild) { //code }
public void DoSomethingBy_MyParent(MyParent myParent) { //code }
但是当我尝试用Action<MyChild>
和DoSomethingBy_MyParent
param对MyParent
委托进行单元测试时,编译器说:
错误CS1503参数1:无法从“父”转换为“我的孩子”。
public void UnitTest()
{
Action<MyChild> processor;
processor = DoSomethingBy_MyChild;
processor(new MyChild()); //OK
processor = DoSomethingBy_MyParent;
processor(new MyChild()); //OK
processor = DoSomethingBy_MyParent;
processor(new MyParent()); //Error
}
来自Using Variance in Delegates (C#):
将方法分配给委托时,协方差和逆变提供了将委托类型与方法签名匹配的灵活性。协方差允许方法具有比委托中定义的更多派生的返回类型。 Contravariance允许一种方法,其参数类型的派生类型少于委托类型中的参数类型。
将DoSomethingBy_MyParent
分配给processor
(由于MyParent
比MyChild
更少衍生而导致的逆变量赋值)是正常的,因为根据定义,任何MyChild
都是MyParent
:
Action<MyChild> processor;
processor = DoSomethingBy_MyParent;
然而,当你试图将MyParent
传递给processor
时会发生什么呢?
Action<MyChild> processor;
processor(new MyParent());
这不是很好,因为processor
需要将MyChild
传递给它 - 它不能被反复调用。你分配DoSomethingBy_MyParent
并不重要 - processor
被声明为Action<MyChild>
所以它必须接收MyChild
或更多派生类型的实例。
换句话说,你有
public void DoSomethingBy_MyChild(MyChild myChild) { //code }
你不希望能够这样称呼它:
DoSomethingBy_MyChild(new Parent());
因为方法调用协同工作(您可以传入更多派生类型的实例),而不是相反(您不能传入较少派生类型的实例)。
这可能有助于Action delegates, generics, covariance and contravariance
基本上,一切都很好。 Action<T>
是逆变的,所以你可以将DoSomethingBy_MyParent
分配给Action<MyChild> processor
。这是相反的。
但由于processor
是Action<MyChild>
类型,你不能用MyParent
实例调用它。