2016-03-15 19 views
5

etwas versucht, wie dies in unserem Code aber es funktioniert nicht:Func Varianz mit mehreren Parametern

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

Die letzten beiden Fälle geben diesen Fehler:

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

Jede Idee, warum?

+1

Da der Rückgabetyp (der letzte allgemeine Parameter von 'Func <>') ist covariant, während die Eingangsparameter (alle die anderen generischen Parameter von 'Func <>') sind kontravariant. – xanatos

+0

Wissenswertes: Konvertierungen von * Methodengruppen * zu Delegaten sind in gleicher Weise auch kovariant und kontravariant. Wenn Sie "Giraffe M (Animal a)" und "delegiere Animal D (Tiger t)" haben, dann ist "D d = M;" in C# zulässig, obwohl D nicht einmal generisch ist. –

Antwort

12

Wenn Sie Func<T, TResult> bei der Unterzeichnung betrachten, sehen Sie, dass die Eingangsparameter (T in diesem Fall) sind kontra und der Rückgabetyp (TResult) ist kovarianten

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

Bei der Kontravarianz geht es grundsätzlich darum, einen "größeren" Typ einer Methode zu übergeben, die einen "kleineren" Typ erwartet, bei dem die Kovarianz genau das Gegenteil ist.

Eric Lippert stellt diese 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.

+0

Es gibt einen Tippfehler in Lipperts ursprünglichem Beitrag. Es sollte "out T" für kovariant sein. – haim770

+2

@ haim770 Ich glaube nicht, dass Eric das meinte. Ich nehme an, er meinte, dass der generische Typ 'I ' in T * kovariant/contravariant * ist, was für jedes 'T' bedeutet. –

+0

Ja. Ich lese den ganzen Artikel und jetzt kann ich erkennen, was er meinte. Danke – haim770

-3

A Person ist keine Employee

Es gibt keinen Guss möglich zwischen Func<Employee, xxx> und Func<Person, xxx>

2

Da Func<T, TResult> ist ein definiert als

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

Wie Sie sehen können, der zweite Parameter (TResult) ist in der Tat ein covariant, aber der erste Parameter (T, die die Eingabe der Funktion ist) ist eigentlich eine kontravariante (Sie können es nur mit etwas, das weniger abgeleitet ist) füttern.

Func<Employee, Person> ist in Ordnung, weil es die Signatur übereinstimmt, während Func<Person, Person> schlägt fehl, weil es nicht ist.

Siehe MSDN

0

Ok, ich glaube, ich verstehe es jetzt:

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