2012-06-18 27 views
9

Tôi đã đọc về những thay đổi mà .NET4.5 sẽ mang lại, và trên this bài đăng trên blog tôi tình cờ gặp một điều mà tôi không biết cũng không hiểu.Ngôn ngữ C# ngăn chặn các generics bị covarient như thế nào trừ khi chúng không chứa các phương thức yêu cầu T làm đầu vào?

Khi nói về việc thực hiện các bộ sưu tập readonly, Immo Landwerth nói:

Thật không may, hệ thống kiểu của chúng tôi không cho phép làm loại T hiệp biến trừ khi nó không có phương pháp mà phải mất T như một đầu vào. Do đó, chúng tôi không thể thêm phương thức IndexOf vào IReadOnlyList. Chúng tôi tin rằng đây là một sự hy sinh nhỏ so với việc không có sự hỗ trợ cho hiệp phương sai.

Từ sự hiểu biết rõ ràng giới hạn của tôi, nó có vẻ như ông đang nói rằng để cho phép chúng tôi để gọi một phương pháp mà đòi hỏi một IReadOnlyList<Shape> bằng cách đi qua trong một IReadOnlyList<Circle>, chúng ta không thể có một phương pháp IReadOnlyList<T>.IndexOf(T someShape).

Tôi không thấy hệ thống kiểu sẽ ngăn chặn điều đó như thế nào. Ai đó có thể giải thích?

Trả lời

11

Giả sử Circle triển khai IEquatable<Circle>. Điều đó sẽ tự nhiên được sử dụng bởi IReadOnlyList<Circle>.IndexOf nếu nó có sẵn. Bây giờ nếu bạn có thể viết này:

IReadOnlyList<Circle> circles = ...; 
IReadOnlyList<Shape> shapes = circles; 
int index = shapes.IndexOf(new Square(10)); 

rằng sẽ kết thúc cố gắng để vượt qua một Square để Circle.Equals(Circle) mà rõ ràng sẽ là một ý tưởng tồi.

Quy tắc thực thi "không có giá trị T ở vị trí nhập" nằm trong phần 13.1.3 của thông số C# 4. Bạn cũng nên đọc Eric Lippert's blog series on generic variance để biết thêm chi tiết .

+0

Đủ công bằng. Tôi đã có thể nghĩ rằng IndexOf sẽ chỉ trở lại -1 nếu nó nhận được một loại không chính xác. Dunno tại sao tôi nghĩ vậy, điều đó có nghĩa là cần phải kiểm tra kiểu nào đó trong từng phương pháp. Tôi sẽ đi thẳng vào các bài đăng trên blog của eric. Cậu bé nhưng người đàn ông đó có thể giải thích điều gì đó theo cách tôi có thể hiểu! –

+0

Những gì một biến thể 'IReadOnlyList 'có thể thực hiện sẽ là' IndexOf (TT nó) ', trong đó kiểu tham số của phương thức độc lập với bộ sưu tập. Một lớp 'List ' có thể thực thi 'IndexOf' của nó sau đó có thể sử dụng ủy nhiệm được tạo ra (được lưu trữ) một cách lười biếng cho mỗi loại' TT', để nếu 'TItem' thực hiện' IEquatable . phương thức tĩnh sẽ gọi đó, và ngược lại, nó sẽ tạo một đại biểu cho một phương thức tĩnh sử dụng một số so sánh khác hoặc, đối với một số kiểu kết hợp 'TT' và' T', đơn giản trả về -1. – supercat

1

Lưu ý rằng nó sẽ là đúng, về mặt lý thuyết (không chắc chắn nếu điều đó được thực hiện trong .NET nào), để xác định IndexOf cho tất cả các phân nhóm của T:

class ReadOnlyList<+T> = { ... 
    Int IndexOf<U>(U elem) where T : U { ... } 
} 

Nó là thú vị để xem tại sao IndexOf(T elem) sẽ không rõ ràng là biến thể, trong khi điều này là: nếu bạn có T2 : T1, một phương pháp IndexOf(T1 elem) có thể không tương thích với chữ ký IndexOf(T2 elem). Ngược lại, nếu bạn có IndexOf<U>(U elem) where T1 : U, bạn biết rằng T2 : T1T1 : U, vì vậy bạn cũng có T2 : U cho tất cả U như vậy: loại này là loại phụ của IndexOf<U>(U elem) with T2 : U.

Nhận xét này được thực hiện, ví dụ, trong bài báo năm 2006 Variance and Generalized Constraints for C# Generics của Emir, Kennedy, Russo và Yu: chúng trình bày một hệ thống kiểu chấp nhận định nghĩa này.

+0

Tôi không nghĩ rằng tham số của 'IndexOf' chung sẽ bị hạn chế. Hoàn toàn hợp lý khi yêu cầu một 'Danh sách ' cho dù nó có chứa một trường hợp đã cho của 'Động vật', hay' Xiêm La', hay thậm chí là 'Con chó'. – supercat

+0

Tất cả các ví dụ đó sẽ được cho phép với chữ ký mà tôi đã cung cấp, sử dụng cách gõ con vào đối số hoặc trên chính danh sách. 'SiameseCat' là một supertype của' Cat' để bạn có thể trực tiếp sử dụng 'List .indexOf '. Một 'List ' cũng là một 'Danh sách ' để bạn có thể sử dụng 'Danh sách .indexOf ' hoặc 'Danh sách .indexOf '. Việc so sánh sẽ được thực hiện ở lớp siêu phổ biến chính xác nhất của hai đối tượng. – gasche

+0

Quan điểm của tôi là không có gì sai với 'ReadableList .IndexOf '. Nếu phương thức 'foo' trong một lớp với các tham số kiểu generic' T' và 'U' nhận một danh sách' ReadableList ' và 'U item', và muốn biết nếu' list' chứa 'item', truy vấn đó sẽ có ý nghĩa nếu 'T' là một phân lớp hoặc siêu lớp của' U', và sẽ có ngữ nghĩa được xác định rõ ngay cả khi các loại không liên quan. Truy vấn không yêu cầu 'foo' để xác định bất kỳ mối quan hệ lớp nào giữa' T' và 'U', đặc biệt là vì các loại danh sách và mục thực tế có thể cho phép phương thức IndexOf ... – supercat

6

IReadOnlyList<T> là biến thể, bạn có thể truyền nó sang bất kỳ siêu kiểu nào là T và tất cả các phương pháp vẫn hoạt động theo hợp đồng. Siêu mẫu siêu đẳng nhất của TObject, vì vậy nếu IndexOf là một phần của giao diện, nó phải chấp nhận Object.

Khi Jon Skeet tuyên bố, trong danh sách các đối tượng Circle, bạn có thể hỏi: chỉ mục của một số Square cụ thể trong danh sách này là gì? Câu trả lời chính xác duy nhất sẽ là "nó không có ở đây" và IndexOf phải trả về -1 và không ném ngoại lệ.

Vì vậy, tôi không đồng ý với Jon Skeet. Với những hạn chế của các thông số chung hiệp biến, và tương tự như ArrayList.indexOf in Java, đội BCL nên tôi đã bao gồm một phương pháp IndexOf với chữ ký sau đây:

int IndexOf(object item); 

Chính xác lập luận tương tự áp dụng cho Contains trong IReadOnlyCollection: khi bạn vượt qua trong một đối tượng của một loại không tương thích, bộ sưu tập rõ ràng không chứa nó và phương pháp chỉ cần trả lại false.

Nhược điểm duy nhất sẽ là đánh đấm các loại giá trị, nhưng thực hiện thực tế vẫn có thể ẩn phương pháp IReadOnlyList.IndexOf và cung cấp quá tải chung chung của chúng, làm cho đối số này tranh luận.

Vì vậy, bạn đúng với mong đợi IndexOf để trả về -1 khi chuyển một đối tượng không tương thích, nếu nó nằm trên giao diện.


Tôi đã thực hiện nguyên tắc này trong thư viện M42.Collections của mình để hiển thị cách thức này sẽ hoạt động trong thực tế. Bạn có thể tải xuống tại đây:

M42 Collections - Thư viện .NET di động để làm việc với bộ sưu tập phù hợp.

+0

Tham số cho 'Bằng’ là vui, trong đó từ quan điểm hiệu suất, nó có thể efit không phải là chung chung, nhưng từ một quan điểm ngữ nghĩa nó là cả hai biến thể và contravariant (có nghĩa là không thực sự chung chung). Hoàn toàn hợp lý khi xem 'Danh sách ' như là một "điều có thể được hỏi nếu nó chứa một' Xiêm "hay" điều ... được cho là "Động vật", hoặc thậm chí là "điều ... đã cho' Con chó " ". Câu trả lời trong trường hợp thứ hai sẽ là "không", nhưng điều đó không có nghĩa là câu hỏi không thể được hỏi. – supercat