2016-03-15 36 views
5

Cố gắng một cái gì đó như thế này trong mã của chúng tôi nhưng nó không thành công:Func không phù hợp với nhiều tham số

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

Hai trường hợp cuối cùng cho lỗi này:

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

Bất cứ ý tưởng tại sao?

+1

Bởi vì kiểu trả về (các tham số chung cuối cùng của 'Func <>') là hiệp biến, trong khi các thông số đầu vào (tất cả các tham số chung khác của 'Func <>') là contravariant. – xanatos

+0

Thực tế thú vị: các chuyển đổi của * nhóm phương thức * đối với các đại biểu cũng là biến thể và biến đổi theo cùng một cách. Nếu bạn có 'Giraffe M (Animal a)' và 'delegate Animal D (Tiger t)' thì 'D d = M;' là hợp pháp trong C#, mặc dù D thậm chí không chung chung. –

Trả lời

12

Nếu bạn nhìn vào chữ ký cho Func<T, TResult>, bạn sẽ thấy rằng các tham số đầu vào (T trong trường hợp này) là contravariant và loại trả lại (TResult) là covariant

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

Contravariance về cơ bản là có thể chuyển loại "lớn hơn" sang phương thức mong đợi kiểu "nhỏ hơn", trong đó hiệp phương sai chính xác.

Eric Lippert đặt này 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ó lỗi đánh máy trong bài đăng gốc của Lippert. Nó phải là 'out T' cho covariant. – haim770

+2

@ haim770 Tôi không nghĩ đó là ý của Eric. Tôi giả định anh ta có nghĩa là kiểu generic 'I ' là covariant/contravariant * trong T *, có nghĩa là cho mọi 'T'. –

+0

Có. Tôi đọc toàn bộ bài viết và bây giờ tôi có thể nhận ra ý của anh ấy. Cảm ơn – haim770

-3

Một Person không phải là một Employee

Không có dàn diễn viên có thể có giữa Func<Employee, xxx>Func<Person, xxx>

2

Func<T, TResult> là một định nghĩa là

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

Như bạn thấy, tham số thứ hai (TResult) thực sự là một hiệp biến, nhưng các tham số đầu tiên (T, là đầu vào của hàm này) thực sự là một contravariant (bạn chỉ có thể nạp nó với cái gì đó ít bắt nguồn hơn).

Func<Employee, Person> là tốt vì nó phù hợp với chữ ký, trong khi Func<Person, Person> không thành công vì không.

Xem MSDN

0

Ok, tôi nghĩ rằng tôi hiểu ngay:

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;} } 
Các vấn đề liên quan