2015-02-28 13 views
11

Tôi đã xác định các loại đại biểu sau. Một trả về một chuỗi, và một một đối tượng:Tại sao ví dụ sau đây sử dụng hiệp phương sai trong các đại biểu không biên dịch?

delegate object delobject(); 
delegate string delstring(); 

Bây giờ xem xét đoạn mã sau:

delstring a =() => "foo"; 
delobject b = a; //Does not compile! 

Tại sao việc chuyển nhượng không hợp lệ?

Tôi không hiểu. Phương thức trả về một chuỗi nên được xem xét một cách an toàn như một phương thức trả về một đối tượng (vì một chuỗi là một đối tượng).


Trong C# 4.0, ví dụ sau hoạt động. Thay vì sử dụng các đại biểu, tôi sử dụng Func<TResult> kiểu chung chung:

Func<string> a =() => "foo"; 
Func<object> b = a; //Perfectly legal, thanks to covariance in generics 

Ngoài ra: nếu tôi viết lại nó như vậy, nó hoạt động:

delobject b =() => a(); 

Nhưng đây không phải là điều tương tự như những gì tôi muốn ban đầu . Bây giờ tôi đã tạo ra một phương pháp mới gọi một phương thức khác.

Nó không chỉ là một bài tập, như trong ví dụ này:

delint a =() => 5; 
delobject b = a; //Does not work, but this is OK, since "int" is a value type. 
delobject b =() => a(); //This will box the integer to an object. 
+0

Làm thế nào nó sẽ có tác dụng nếu các đại biểu chấp nhận các thông số? Tôi đoán cho sự nhất quán các nhà thiết kế ngôn ngữ quyết định các quy tắc nên giống nhau khi không có tham số. – JohnLBevan

+0

@JohnLBevan Cùng một cách? 'Func ' hoàn toàn có thể được gán cho một 'Func '. – hvd

+0

"Thay vì sử dụng đại biểu" không hoàn toàn chính xác, vì 'Func ' là loại ** ủy quyền chung **. –

Trả lời

11

Loại đại biểu vẫn là loại đối tượng cụ thể.Khi bạn viết

delegate object delobject(); 
delegate string delstring(); 

thì không có mối quan hệ "là" giữa hai loại đại biểu. Bạn vừa tạo hai loại riêng biệt.

Nó giống như

class A { public int i; }; 
class B { public int i; }; 

Không có chuyển đổi ngầm hay rõ ràng giữa AB, mặc dù không có bất kỳ khả năng A rằng sẽ không có ý nghĩa tương đương như một B, hoặc ngược lại.

Cỏ và contravariance trong Func có nghĩa là tác giả của loại đại biểu bê tông đã quyết định rằng Func<string> có thể được coi là Func<object>, nhưng đó là điều mà tác giả của loại đại biểu được quyết định, giống như đó là tác giả của lớp học của tôi B để quyết định xem liệu có lẽ sẽ có ý nghĩa hơn nếu chỉ lấy được từ A.

Something bạn thể làm là thêm vào một mức gián tiếp mà không cần tạo một phương pháp bổ sung:

delstring a =() => "foo"; 
delobject b = new delobject(a); // or equivalently, a.Invoke 
+1

Nếu đó là một ủy nhiệm dàn diễn viên duy nhất và bạn có đủ quyền, bạn cũng có thể sử dụng '(delobject) Delegate.CreateDelegate (typeof (delobject), a.Target, a.Method)', tránh thụt vào. – CodesInChaos

+0

@CodesInChaos Bạn nói đúng, nhưng liệu nó có đáng để nỗ lực không chỉ phụ thuộc vào việc bạn có đủ quyền hay không, mà còn về tần suất đại biểu sẽ được gọi, phải không? Những gì bạn có là một cách đắt tiền hơn để tạo ra một đại biểu sẽ rẻ hơn để gọi. – hvd

+0

@CodesInChaos: FWIW, đối tác của đại biểu "multicast" là "unicast", không phải "dàn diễn viên duy nhất" –

5

Loại lý luận về sự Func được đánh dấu inout, vì vậy bạn có thể gán các loại từ một đến khác như bạn làm. Đây là phương sai của phương pháp trong generics.

delobjectdelstring là các loại khác nhau và chúng không phải là biến thể, do đó bạn không thể chỉ định cho nhau. Nhưng với delobject, bạn có thể gán một trình xử lý trả về kiểu dẫn xuất, đây là phương thức khác nhau trong các đại biểu.

3

Delegates (MSDN):

Trong bối cảnh phương pháp quá tải, có chữ ký của một phương pháp không bao gồm giá trị trả về. Nhưng trong bối cảnh của các đại biểu, chữ ký không bao gồm giá trị trả lại. Nói cách khác, phương thức phải có cùng loại trả về làm đại biểu.

Kể từ khi kiểu trả về không chính xác trận đấu (ví dụ: object != string), việc chuyển nhượng không hợp lệ.

Vì vậy, bạn có thể có

delstring a =() => "foo"; // compiles 
delobject b = a;   // does not compile 
delobject c =() => "foo"; // compiles 

Như bạn chỉ ra, Func làm việc covariantly sử dụng out, trong khi các đại biểu thì không.

+1

Tài liệu đó gây hiểu lầm, một phương pháp không cần phải có cùng kiểu trả về với tư cách là người được ủy quyền. Bạn có thể chuyển đổi một phương thức trả về 'chuỗi' thành' delobject'. Chỉ cần thử 'chương trình lớp tĩnh 'đối tượng đại biểu D(); chuỗi tĩnh f() {return "Xin chào, thế giới!"; } static void Main() {D f = Program.f; Console.WriteLine (f()); }} 'cho một ví dụ đơn giản. – hvd

+0

@hvd bạn nói đúng, tài liệu có chút ít gây hiểu nhầm. Ví dụ 'c' của tôi biên dịch vì' object' là [assignable from] (https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom) 'string' (hoặc, công bằng, bất cứ điều gì khác) - ví dụ của bạn hoạt động theo cùng một cách. –

+0

Các yêu cầu có một chút chặt chẽ hơn so với "có thể gán từ", nhưng điều đó sẽ đến gần. (Nó sẽ không hoạt động đối với bất kỳ loại nào mà chuyển đổi ngầm thực hiện một số thao tác, ngay cả khi thao tác đó chỉ đơn giản là boxing.) [Sử dụng phương sai trong các đại biểu] (https://msdn.microsoft.com/en-gb/library/ ms173174.aspx) cũng đưa ra một ví dụ. – hvd

Các vấn đề liên quan