委托的逆变导致 "Cannot convert from ... to ..." 的错误

Contravariance of delegate resulting in error of "Cannot convert from ... to ..."

为了简化,假设我有一个 parent 和一个 child class:

public class MyParent { }
public class MyChild : MyParent { }

这两个函数带有一些代码:

public void DoSomethingBy_MyChild(MyChild myChild) { //code }
public void DoSomethingBy_MyParent(MyParent myParent) { //code }

但是当我尝试使用 DoSomethingBy_MyParentMyParent 参数对 Action<MyChild> 委托进行单元测试时,编译器说:

Error CS1503 Argument 1: cannot convert from 'MyParent' to 'MyChild'.

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
}

这可能会有所帮助 Action delegates, generics, covariance and contravariance

基本上,一切都很好。 Action<T> 是逆变的,因此您可以将 DoSomethingBy_MyParent 分配给 Action<MyChild> processor。这就是逆变。

但是因为 processor 是类型 Action<MyChild> 你不能用 MyParent 实例调用它。

来自 Using Variance in Delegates (C#):

When you assign a method to a delegate, covariance and contravariance provide flexibility for matching a delegate type with a method signature. Covariance permits a method to have return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.


可以将 DoSomethingBy_MyParent 分配给 processor 逆变 分配,因为 MyParentMyChild) 因为根据定义,任何 MyChild 也是 MyParent:

Action<MyChild> processor;
processor = DoSomethingBy_MyParent;

但是,当您尝试将 MyParent 传递给 processor 时,实际上发生的情况是

Action<MyChild> processor;
processor(new MyParent());           

这不好,因为 processor 需要将 MyChild 传递给它 - 它不能被 调用 逆变。您已将 DoSomethingBy_MyParent 分配给它并不重要 - processor 被声明为 Action<MyChild> 因此它 必须 接收 [= 的实例17=] 或更派生的类型。


换句话说,你有

public void DoSomethingBy_MyChild(MyChild myChild) { //code }

而且您不会期望能够像这样调用它:

DoSomethingBy_MyChild(new Parent());

因为方法调用是协变的(您可以传入派生程度更高的类型的实例),而不是逆变的(您不能传入派生程度较低的类型的实例)。