具有多个参数的函数方差

Func variance with multiple parameters

在我们的代码中尝试过类似的操作,但失败了:

Func<Employee, Employee> _myFunc;

void Main()
{
    Func<Employee, Employee> test1  = _myFunc;//Ok
    Func<Employee, Person> test2  = _myFunc;//Ok
    Func<Person, Employee> test3 = _myFunc;//Fails
    Func<Person, Person> test4  = _myFunc;//Fails
}

public class Person { }
public class Employee : Person { }

后两种情况报错:

Cannot implicitly convert type System.Func<Employee, Employee> to System.Func<Person, Employee>. An explicit conversion exists (are you missing a cast?)

知道为什么吗?

Person 不是 Employee

Func<Employee, xxx>Func<Person, xxx>

之间没有转换可能

如果您查看 Func<T, TResult> 的签名,您会看到输入参数(在本例中为 T)是 逆变,并且return 类型 (TResult) 是 协变

public delegate TResult Func<in T, out TResult>(T arg);

逆变基本上是关于能够将 "bigger" 类型传递给期望 "smaller" 类型的方法,其中协方差恰好相反。

Eric Lippert beautifully and elegantly (emphasis mine):

A generic type I is covariant (in T) if construction with reference type arguments preserves the direction of assignment compatibility. It is contravariant (in T) if it reverses the direction of assignment compatibility. And it is invariant if it does neither. And by that, we simply are saying in a concise way that the projection which takes a T and produces I is a covariant/contravariant/invariant projection.

因为Func<T, TResult>被定义为

public delegate TResult Func<in T, out TResult>(T arg);

如你所见,第二个参数(TResult)确实是协变的,但是第一个参数(T,它是函数的输入)实际上是逆变的(你只能用衍生较少的东西喂它。

Func<Employee, Person> 很好,因为它仍然匹配签名,而 Func<Person, Person> 失败,因为它不匹配。

MSDN

好的,我想我现在明白了:

void Main()
{
    Func<Employee, Employee> getEmployeesBoss = (Employee employee) => {return employee.Boss;};
    //This works as it expects a Person to be returned and employee.Boss is a person.
    Func<Employee, Person> getEmployeesBoss1 = getEmployeesBoss;
    //This fails as I could pass a non Employee person to this func which would not work.
    Func<Person, Employee> getEmployeesBoss2 = getEmployeesBoss;
}

class Person {} 
class Employee : Person { public Employee Boss{get;set;}    }