2010-08-30 29 views
16

Ví dụ giao diện IEnumerable<T>:Tại sao giao diện chung không đồng/contravariant theo mặc định?

public interface IEnumerable<out T> : IEnumerable 
{ 
    IEnumerator<T> GetEnumerator(); 
} 

Trong giao diện này kiểu generic chỉ được sử dụng như một kiểu trả về của phương thức giao diện và không được sử dụng như một loại đối số phương pháp như vậy, nó có thể được hiệp biến. Đưa ra điều này, không thể trình biên dịch suy luận về mặt phương sai từ giao diện? Nếu có thể, tại sao C# yêu cầu chúng tôi đặt các từ khóa co/contravariance một cách rõ ràng.

Cập nhật: Như Jon Skeet nêu câu hỏi này có thể được spited thành các câu hỏi sau:

  1. Can biên dịch suy luận chung loại của đồng/contravariance bởi cách nó được sử dụng bên trong loại generic hiện tại và tất cả đó là loại cơ sở?

    Ví dụ: Có bao nhiêu tham số giao diện chung từ .NET Framework 4.0 có thể được đánh dấu đồng/contravariant tự động mà không có bất kỳ sự mơ hồ nào? Khoảng 70%, 80%, 90% hoặc 100%?

  2. Nếu có thể, nên áp dụng đồng/contravariance cho loại chung theo mặc định? Ít nhất là với những loại mà nó có khả năng phân tích và phỏng đoán co/contravariance từ việc sử dụng loại.

+2

Có một số câu trả lời tuyệt vời bên dưới về lý do tại sao điều này không phải là tự động. Tôi chỉ muốn thêm rằng ReSharper * sẽ * đề nghị co/contra-variance và thậm chí làm tái cấu trúc cho bạn nếu bạn chấp nhận đề xuất của nó. Tôi không biết nó hoạt động tốt như thế nào trong những tình huống không rõ ràng/khó khăn, mặc dù (tôi cho rằng nó sẽ không cố gắng gợi ý trong những trường hợp đó). –

+1

Câu hỏi hay. Tôi đã dự đoán câu hỏi của bạn vào năm 2007 khi chúng tôi thiết kế tính năng này. Đó là lý do tại sao tôi đã viết một bài viết trả lời nó sau đó: http://blogs.msdn.com/b/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why- Do-we-need-a-syntax-at-all.aspx –

Trả lời

16

Vâng, có hai câu hỏi ở đây. Thứ nhất, có thể trình biên dịch luôn làm như vậy? Thứ hai, nên nó (nếu có thể)?

Đối với câu hỏi đầu tiên, tôi sẽ hoãn đến Eric Lippert, người đã nhận xét này khi tôi mang chính xác vấn đề này lên trong ấn bản lần thứ 2 của C# trong Depth:

Nó không rõ ràng với tôi rằng chúng ta hợp lý thậm chí có thể nếu chúng ta muốn. Chúng ta có thể dễ dàng tìm ra với các tình huống đòi hỏi phải phân tích toàn cầu tất cả các giao diện trong một chương trình để tìm ra các phương sai và chúng ta có thể dễ dàng tìm ra các tình huống trong đó <in T, out U> hoặc <out T, in U> và không có cách nào để quyết định giữa chúng. Với cả hai trường hợp xấu và hiệu suất là , các trường hợp không rõ ràng là một tính năng không chắc chắn.

(Tôi hy vọng Eric không ngại tôi trích dẫn nguyên văn này; anh ấy trước đây đã được rất tốt về việc chia sẻ những hiểu biết như vậy, nên tôi sẽ bằng hình thức quá khứ :)

Mặt khác, tôi nghi ngờ vẫn còn những trường hợp mà nó có thể được suy ra không có sự mơ hồ, do đó, điểm thứ hai vẫn có liên quan ...

Tôi không nghĩ rằng nó phải tự động ngay cả khi trình biên dịch có thể biết rõ ràng rằng nó hợp lệ chỉ một chiều. Trong khi mở rộng một giao diện luôn luôn là một thay đổi phá vỡ ở một mức độ nào đó, thường thì không phải nếu bạn là người duy nhất thực hiện nó. Tuy nhiên, nếu mọi người đang dựa vào giao diện của bạn là biến thể, bạn có thể không phải là có thể để thêm phương thức vào giao diện đó mà không vi phạm khách hàng ... ngay cả khi họ chỉ là người gọi chứ không phải người thực hiện. Các phương thức bạn thêm vào có thể thay đổi giao diện trước đó biến đổi để trở thành bất biến, tại thời điểm đó bạn phá vỡ bất kỳ người gọi nào đang cố gắng sử dụng nó một cách tương đối.Về cơ bản, tôi nghĩ rằng nó là tốt để yêu cầu này phải rõ ràng - đó là một quyết định thiết kế bạn nên làm một cách có ý thức, chứ không chỉ là vô tình kết thúc với hiệp phương sai/contravariance mà không cần suy nghĩ về nó.

+1

Nếu làm cho các kiểu generic hiệp phương sai/contravariance có thể dẫn đến "phá vỡ khách hàng" thì tại sao NET sáng tạo thay thế giao diện chung chung của họ để đồng/contravariant đối tác trong .NET 4.0 ? –

+1

@Koistya: Nó không phải là cách tròn: đó là cách khác vòng. Nếu nó được phỏng đoán là covariant, nhưng sau đó bạn thêm một phương thức làm cho nó trở thành * bất biến *, thì bất kỳ máy khách nào đang sử dụng hiệp phương sai sẽ bị vặn. Tôi sẽ chỉnh sửa để giải thích điều này. –

+3

@Koistya: Tạo các loại bất biến thành các loại biến thể thực tế là một sự thay đổi phá vỡ; chúng tôi đã quyết định rằng lợi ích của sự thay đổi này đáng giá cho nỗi đau của giờ nghỉ. Xem bài viết của tôi về điều đó: http://blogs.msdn.com/b/ericlippert/archive/2007/11/02/covariance-and-contravariance-in-c-part-nine-breaking-changes.aspx - Tuy nhiên, Jon ghi chú chính xác rằng kiểu phá vỡ mà anh ta đang nói đến là * chỉnh sửa một giao diện có thể thay đổi chú thích phương sai hợp pháp bất ngờ *. Do đó, chúng tôi muốn làm cho các chú thích đó rõ ràng hơn là suy luận chúng. –

5

This article giải thích rằng có những tình huống mà các trình biên dịch không thể suy ra và vì vậy nó cung cấp cho bạn với cú pháp rõ ràng:

interface IReadWriteBase<T> 
{ 
    IReadWrite<T> ReadWrite(); 
} 

interface IReadWrite<T> : IReadWriteBase<T> 
{ 

} 

Bạn suy luận gì ở đây in hoặc out, cả hai công việc?

+0

Trình biên dịch có thể áp dụng từ khóa 'out' tại đây theo mặc định cho giao diện cơ sở dựa trên việc sử dụng và giống nhau cho giao diện xuất phát dựa trên phụ huynh T –

+0

Không, cả hai đều có thể. Hãy thử nó :-) –

+0

Đúng, bạn có thể đặt cả theo cách thủ công nhưng từ điểm trình biên dịch xem, nó có thể đặt T là biến thể theo mặc định trong ví dụ của bạn. Có hay không? –

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