2016-03-15 20 views
5

Próbowałem coś takiego w naszym kodzie, ale nie udaje:Func wariancji z wieloma parametrami

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

dwóch ostatnich przypadkach dać ten błąd:

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

jakiś pomysł, dlaczego?

+1

Ponieważ typ zwracany (ostatni generyczny parametr 'Func <>') jest kowariantna, natomiast parametry wejściowe (wszystkie pozostałe ogólne parametry 'Func <>') są sprzeczne. – xanatos

+0

Zabawny fakt: konwersje * grup metod * na delegatów są również kowariantne i sprzeczne w taki sam sposób. Jeśli posiadasz 'Giraffe M (Animal a)' i 'delegate Animal D (Tiger t)', to 'D d = M;' jest legalne w języku C#, chociaż D nie jest nawet generyczne. –

Odpowiedz

12

Jeśli spojrzeć na podpis Func<T, TResult>, zobaczysz, że parametry wejściowe (T w tym przypadku) są kontrawariantny i typ zwrotny (TResult) jest kowariantna

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

kontrawariancja jest w zasadzie o byciu w stanie przekazać „większy” typ sposobu spodziewa się „mniejszy” typ, gdzie kowariancja jest dokładnie odwrotnie.

Eric Lippert stawia ten 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

W oryginalnym poście Lipperta jest literówka. Powinno to być "out T" dla kowariancji. – haim770

+2

@ haim770 Nie sądzę, że to właśnie miał na myśli Eric. Zakładam, że miał na myśli, że ogólny typ 'I ' jest kowariantny/contravariant * w T *, co oznacza dla każdego 'T'. –

+0

Tak. Przeczytałem cały artykuł i teraz mogę zrozumieć, co miał na myśli. Dziękuję – haim770

-3

Person nie jest Employee

Nie ma obsada możliwe między Func<Employee, xxx> i Func<Person, xxx>

2

Ponieważ Func<T, TResult> jest zdefiniowany jako

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

Jak widać, drugi parametr (TResult) jest rzeczywiście kowariantna, ale pierwszy parametr (T, który jest wejściem funkcji) jest tak naprawdę kontrawariantem (można go tylko karmić czymś, co jest mniej pochodne).

Func<Employee, Person> jest w porządku, ponieważ parapet pasuje do podpisu, a Func<Person, Person> kończy się niepowodzeniem, ponieważ tak nie jest.

Zobacz MSDN

0

Ok, myślę, że rozumiem go teraz:

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;} } 
Powiązane problemy