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?
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.
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
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;} }
A Person
is not an Employee
There is no cast possible between Func<Employee, xxx>
and Func<Person, xxx>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.