2008-10-21 55 views
32

Đây có thể là câu hỏi cũ: Tại sao IEnumerable<T> được kế thừa từ IEnumerable?Tại sao IEnumerable <T> kế thừa từ IEnumerable?

Đây là cách .NET làm, nhưng nó mang lại một chút rắc rối. Mỗi khi tôi viết một lớp thực hiện IEumerable<T>, tôi phải viết hai hàm GetEnumerator(), một cho IEnumerable<T> và một cho IEnumerable.

Và, IList<T> không được kế thừa từ IList.

Tôi không biết tại sao IEnumerable<T> được thiết kế theo cách khác.

Trả lời

48

Straight from the horse's mouth (Hejlsberg):

Lý tưởng nhất là tất cả các giao diện bộ sưu tập chung (ví dụ ICollection<T>, IList<T>) sẽ kế thừa từ các đối tác phi chung của họ như vậy trường hợp giao diện chung có thể được sử dụng cả hai với generic và phi mã chung. Ví dụ, nó sẽ thuận tiện nếu một IList<T> có thể được chuyển đến mã mong đợi một IList.

Khi nó quay ra, chỉ có giao diện chung cho mà điều này có thể là IEnumerable<T>, bởi vì chỉ có IEnumerable<T> là trường hợp bị biến thể: Trong IEnumerable<T>, loại thông số T chỉ được sử dụng ở các vị trí "đầu ra" (giá trị trả lại) và không ở vị trí "đầu vào" (tham số). ICollection<T>IList<T> sử dụng T ở cả vị trí đầu vào và đầu ra, và do đó các giao diện đó là bất biến. (Là một sang một bên, họ sẽ là ngược biến nếu T chỉ được sử dụng ở các vị trí đầu vào, nhưng điều đó không thực sự quan trọng ở đây.)

< ... snip ...>

Vì vậy, để trả lời câu hỏi của bạn, IEnumerable<T> được kế thừa từ IEnumerable vì có thể! :-)

+1

Thật thú vị ... Tôi đã thực sự bực mình rằng 'IList ' không kế thừa 'IList', nhưng ngay sau khi bạn đưa phương sai vào tài khoản, nó bắt đầu có ý nghĩa ... –

+1

Quá xấu Microsoft chưa bao giờ định nghĩa IReadableByIndex và IReadableByIndex (Of Out T), có thể đã được thừa kế bởi IList và IList (Of T). IReadableByIndex có thể được kế thừa bởi IReadableByIndex (Of T) mà không gặp khó khăn, và phương sai của các bộ sưu tập chỉ mục có thể đọc được sẽ rất đẹp. – supercat

+0

@supercat Microsoft có thể đã nghe những lời cầu nguyện của bạn, bởi vì kể từ khi bạn viết nhận xét đó, họ đã giới thiệu ['IReadOnlyList '] (http://msdn.microsoft.com/en-us/library/hh192385.aspx) trong đó 'out' biểu thị hiệp phương sai trong' T'. –

16

Câu trả lời cho IEnumerable là: "vì nó có thể không ảnh hưởng đến an toàn loại".

IEnumerable là giao diện "chỉ đọc" - vì vậy không quan trọng là biểu mẫu chung cụ thể hơn dạng không nongener. Bạn không phá vỡ bất cứ điều gì bằng cách thực hiện cả hai. IEnumerator.Current trả về object, trong khi IEnumerator<T>.Current trả về T - không sao, vì bạn luôn có thể chuyển đổi hợp pháp thành object, mặc dù nó có thể có nghĩa là quyền anh.

Hãy so sánh điều này với IList<T>IList - bạn có thể gọi Add(object) trên một IList, trong khi đó cũng có thể là không hợp lệ cho bất kỳ đặc biệt IList<T> (bất cứ điều gì khác hơn là IList<object> trên thực tế).

Brad Abram blogged with Anders' answer về câu hỏi này.

2

Điều này là để nó hoạt động với các lớp không hỗ trợ Generics. Ngoài ra, Generics NET không cho phép bạn làm những việc như cast IList < long> như IList < int>, vì vậy các phiên bản không chung của giao diện có thể khá hữu ích khi bạn cần một lớp cơ sở cố định hoặc giao diện.

+0

Đây là điểm quan trọng. Không thể chuyển IList sang IList . –

3

Tính năng tương thích ngược. Nếu bạn gọi một .Net 1.1 chức năng mà mong đợi một vani IEnumerable bạn có thể vượt qua trong IEnumerable chung của bạn.

Luckilly các IEnumerator chung được thừa hưởng từ kiểu cũ IEnumerator

tôi thường thực hiện một phương pháp tin trả về một Enumerator và sau đó vượt qua nó cho cả hai phương pháp phong cách GetEnumerator cũ và mới.

private IEnumerator<string> Enumerator() { 
     // ... 
    } 

    public IEnumerator<string> GetEnumerator() { 
     return Enumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
     return Enumerator(); 
    } 
+0

Khi triển khai IEnumerable , bạn cũng cần phải thực hiện phương thức IEnumerable.GetEnumerator() riêng. May mắn thay, mã ở trên hoạt động tốt, vì vậy bạn không phải nhân đôi nỗ lực. – spoulson

+2

@spoulson: Không phải riêng tư. Đó là một phần của giao diện, vì vậy nó phải được triển khai công khai. Ví dụ của Mendelt không thực hiện nó một cách riêng tư, nó thực hiện nó một cách rõ ràng, để nó chỉ có sẵn khi đối tượng được đưa vào IEnumerable. –

+0

@spoulson: Hai phương pháp thứ hai được tạo ra khi tôi nhấp vào giao diện triển khai trong studio trực quan, không thực sự nhìn vào nó. Bạn chỉ có thể thêm một cách sử dụng cho System.Collections và làm cho hàm thứ ba được công khai một cách rõ ràng. – Mendelt

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