问题
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>
toSystem.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