Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Func variance with multiple parameters

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?

like image 404
Carra Avatar asked Mar 15 '16 09:03

Carra


2 Answers

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.

like image 50
Yuval Itzchakov Avatar answered Nov 20 '22 12:11

Yuval Itzchakov


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

like image 41
haim770 Avatar answered Nov 20 '22 12:11

haim770