Func variance with multiple parameters

半世苍凉 提交于 2019-12-07 08:10:34

问题


Tried to something like this in our code but it fails:

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 { }

The last two cases give this error:

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

Any idea why?


回答1:


If you look at the signature for Func<T, TResult>, you'll see that the input parameters (T in this case) are contravariant, and the return type (TResult) is covariant

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

Contravariance is basically about being able to pass a "bigger" type to a method expecting a "smaller" type, where covariance is exactly the opposite.

Eric Lippert puts this 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.




回答2:


Because Func<T, TResult> is a defined as

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

As you can see, the second parameter (TResult) is indeed a covariant, but the first parameter (T, which is the input of the function) is actually a contravariant (you can only feed it with something that is less-derived).

Func<Employee, Person> is fine because it sill matches the signature, while Func<Person, Person> fails because it isn't.

See MSDN




回答3:


Ok, I think I understand it now:

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;}    }



回答4:


A Person is not an Employee

There is no cast possible between Func<Employee, xxx> and Func<Person, xxx>



来源:https://stackoverflow.com/questions/36006997/func-variance-with-multiple-parameters

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!