2016-03-15 34 views
5

cercato di qualcosa di simile nel nostro codice ma non riesce:Funz contrasto con più parametri

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

Gli ultimi due casi che danno questo errore:

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

Qualsiasi idea del perché?

+1

Poiché il tipo di ritorno (l'ultimo parametro generico di 'Func <>') è covariante, mentre i parametri di input (tutti gli altri parametri generici di 'Func <>') sono controvarianti. – xanatos

+0

Fatto divertente: le conversioni dei * gruppi di metodi * ai delegati sono anche covarianti e controvarianti allo stesso modo. Se hai 'Giraffe M (Animale a)' e 'delegato Animale D (Tigre t)' allora 'D d = M;' è legale in C#, anche se D non è nemmeno generico. –

risposta

12

Se si guarda alla firma per Func<T, TResult>, vedrai che i parametri di input (T in questo caso) sono controvarianti, e il tipo di ritorno (TResult) è covariante

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

La controvarianza consiste fondamentalmente nell'essere in grado di passare un tipo "più grande" a un metodo che si aspetta un tipo "più piccolo", dove la covarianza è esattamente l'opposto.

Eric Lippert mette questa 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

C'è un refuso nel post originale di Lippert. Dovrebbe essere "fuori T" per covariante. – haim770

+2

@ haim770 Non penso che sia quello che intendeva Eric. Presumo che intendesse che il tipo generico 'I ' è covariante/controvariante * in T *, che significa per ogni 'T'. –

+0

Sì. Ho letto l'articolo completo e ora capisco cosa voleva dire. Grazie – haim770

-3

Un Person non è un Employee

Non v'è alcuna fusione possibile tra Func<Employee, xxx> e Func<Person, xxx>

2

Perché Func<T, TResult> è un definito come

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

Come si può vedere, il secondo parametro (TResult) è davvero un covariante, ma il primo parametro (T, che è l'input della funzione) è in realtà un controvariante (è possibile alimentarlo solo con qualcosa che è meno derivato).

Func<Employee, Person> va bene perché il davanzale corrisponde alla firma, mentre Func<Person, Person> non riesce perché non lo è.

Vedi MSDN

0

Ok, credo di aver capito subito:

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