2011-01-27 26 views
66

List khai từ MSDN:Tại sao (thực sự nó?) Danh sách <T> thực hiện tất cả các giao diện này, không chỉ IList <T>?

public class List<T> : IList<T>, ICollection<T>, 
IEnumerable<T>, IList, ICollection, IEnumerable 

Reflector cho bức tranh tương tự. Có List thực sự thực hiện tất cả những điều này (nếu có lý do)? Tôi đã kiểm tra:

interface I1 {} 
    interface I2 : I1 {} 
    interface I3 : I2 {} 

    class A : I3 {} 
    class B : I3, I2, I1 {} 

    static void Main(string[] args) 
    { 
     var a = new A(); 
     var a1 = (I1)a; 
     var a2 = (I2)a; 
     var a3 = (I3)a; 

     var b = new B(); 
     var b1 = (I1) b; 
     var b2 = (I2)b; 
     var b3 = (I3)b; 
    } 

nó biên dịch.

[CẬP NHẬT]:

Guys, như tôi hiểu, tất cả các câu trả lời ở đó:

class Program 
{ 

    interface I1 {} 
    interface I2 : I1 {} 
    interface I3 : I2 {} 

    class A : I3 {} 
    class B : I3, I2, I1 {} 

    static void I1M(I1 i1) {} 
    static void I2M(I2 i2) {} 
    static void I3M(I3 i3) {} 

    static void Main(string[] args) 
    { 
     var a = new A(); 
     I1M(a); 
     I2M(a); 
     I3M(a); 

     var b = new B(); 
     I1M(b); 
     I2M(b); 
     I3M(b); 

     Console.ReadLine(); 
    } 
} 

sẽ cung cấp cho lỗi, nhưng nó biên dịch và chạy mà không có bất kỳ lỗi nào. Tại sao?

+2

Có danh sách thực hiện tất cả các giao diện đó, tại sao - bởi vì tất cả chúng đều có liên quan đến bộ sưu tập danh sách. Mã của bạn là gì? Đó có phải là một câu hỏi khác không? –

+0

mẫu mã của bạn không có gì để làm với các Danh sách lớp '' ... – BoltClock

+0

Tôi nghĩ anh ta hỏi tại sao 'công cộng Danh sách lớp : IList , ICollection , IEnumerable , etc.' thay vì chỉ' Danh sách public class : IList '. Lưu ý rằng 'ICollection' và' IList' (những cái không chung) không được thừa kế từ 'IList '. – Rup

Trả lời

129

CẬP NHẬT: Câu hỏi này là cơ sở của my blog entry for Monday April 4th 2011. Cảm ơn vì câu hỏi tuyệt vời của bạn.

Hãy để tôi chia nhỏ thành nhiều câu hỏi nhỏ hơn.

List<T> thực sự triển khai tất cả các giao diện đó không?

Có.

Tại sao?

Bởi vì khi một giao diện (nói, IList<T>) được thừa hưởng từ một giao diện (nói IEnumerable<T>) sau đó người thực hiện của giao diện có nguồn gốc hơn được yêu cầu cũng để thực hiện các giao diện ít có nguồn gốc. Đó là ý nghĩa của giao diện; nếu bạn thực hiện hợp đồng của loại có nguồn gốc nhiều hơn thì bạn cũng phải thực hiện hợp đồng của loại có nguồn gốc ít hơn.

Vì vậy, một lớp học được yêu cầu triển khai tất cả các phương pháp của tất cả các giao diện trong giao diện cơ bản của giao diện cơ sở?

Chính xác.

Một lớp thực hiện giao diện có nguồn gốc nhiều hơn cũng cần phải nêu trong danh sách kiểu cơ sở của nó là nó đang triển khai tất cả các giao diện ít xuất phát đó?

số

là lớp cần thiết để không nêu nó?

số

Vì vậy, nó tùy chọn liệu các giao diện thực hiện ít có nguồn gốc từ được nêu trong danh sách loại hình cơ bản?

Có.

Luôn luôn?

Hầu như luôn luôn:

interface I1 {} 
interface I2 : I1 {} 
interface I3 : I2 {} 

Nó là tùy chọn cho dù I3 nói rằng nó được thừa hưởng từ I1.

class B : I3 {} 

Người triển khai I3 được yêu cầu triển khai I2 và I1, nhưng không bắt buộc phải nêu rõ là họ đang làm như vậy. Nó là tùy chọn.

class D : B {} 

Các lớp bắt buộc không bắt buộc phải nói lại rằng chúng triển khai giao diện từ lớp cơ sở của chúng, nhưng được phép làm như vậy. (Trường hợp này là đặc biệt; xem bên dưới để biết thêm chi tiết.)

class C<T> where T : I3 
{ 
    public virtual void M<U>() where U : I3 {} 
} 

Đối số kiểu tương ứng với T và U được yêu cầu triển khai I2 và I1, nhưng không bắt buộc đối với các ràng buộc trên T hoặc U.

Nó luôn luôn là không bắt buộc phải tái trạng thái bất kỳ giao diện cơ sở trong một lớp học phần:

partial class E : I3 {} 
partial class E {} 

Phần thứ hai của E được phép nói rằng nó thực hiện I3 hoặc I2 hoặc I1, nhưng không bắt buộc phải làm vì thế.

OK, tôi hiểu; nó là tùy chọn. Tại sao bất cứ ai không cần thiết phải nêu một giao diện cơ bản?

Có lẽ vì họ tin rằng làm như vậy giúp mã dễ hiểu hơn và tự tài liệu hơn.

Hoặc, có lẽ các nhà phát triển viết mã như

interface I1 {} 
interface I2 {} 
interface I3 : I1, I2 {} 

và nhận ra, oh, chờ một phút, I2 nên kế thừa từ I1.Tại sao nên thực hiện chỉnh sửa đó sau đó yêu cầu nhà phát triển quay trở lại và thay đổi tuyên bố của I3 thành không phải có đề cập rõ ràng về I1? Tôi thấy không có lý do để buộc các nhà phát triển để loại bỏ thông tin dư thừa.

Bên cạnh là dễ dàng hơn để đọc và hiểu, là có bất kỳ kỹ thuật chênh lệch giữa nêu một giao diện một cách rõ ràng trong danh sách loại cơ sở và để lại nó unstated nhưng ngụ ý?

Thường thì không, nhưng có thể có sự khác biệt tinh tế trong một trường hợp. Giả sử bạn có một lớp dẫn xuất D có lớp cơ sở B đã triển khai một số giao diện. D tự động thực hiện các giao diện đó thông qua B. Nếu bạn tái xác nhận các giao diện trong danh sách lớp cơ sở của D thì trình biên dịch C# sẽ thực hiện một giao diện tái triển khai. Các chi tiết có một chút tinh tế; nếu bạn quan tâm đến cách làm việc này thì tôi khuyên bạn nên đọc kỹ phần 13.4.6 của đặc tả C# 4.

Mã nguồn List<T> có thực sự nêu tất cả các giao diện đó không?

số Mã nguồn thực tế nói

public class List<T> : IList<T>, System.Collections.IList 

Tại sao MSDN có danh sách giao diện đầy đủ nhưng mã nguồn thực không?

Vì MSDN là tài liệu; nó phải cung cấp cho bạn nhiều thông tin như bạn muốn. Nó là rõ ràng hơn cho các tài liệu được hoàn thành tất cả ở một nơi hơn để làm cho bạn tìm kiếm thông qua mười trang khác nhau để tìm ra những gì giao diện đầy đủ thiết lập được.

Tại sao trình phản chiếu hiển thị toàn bộ danh sách?

Trình phản xạ chỉ có siêu dữ liệu để hoạt động. Kể từ khi đưa vào danh sách đầy đủ là tùy chọn, Reflector không có ý tưởng cho dù mã nguồn ban đầu có chứa danh sách đầy đủ hay không. Nó là tốt hơn để err ở phía bên của thông tin nhiều hơn nữa. Một lần nữa, Reflector đang cố gắng giúp bạn bằng cách hiển thị cho bạn nhiều thông tin hơn là ẩn thông tin bạn có thể cần.

THƯỞNG HỎI: Tại sao IEnumerable<T> kế thừa từ IEnumerable nhưng IList<T> không kế thừa từ IList?

Một chuỗi các số nguyên có thể được coi là một chuỗi các đối tượng, bằng cách giải nén mọi số nguyên khi nó xuất hiện trong chuỗi. Nhưng một danh sách đọc-viết của các số nguyên không thể được coi là một danh sách đọc-ghi các đối tượng, bởi vì bạn có thể đặt một chuỗi vào một danh sách đọc-ghi các đối tượng. An IList<T> không bắt buộc phải hoàn thành toàn bộ hợp đồng IList, vì vậy nó không được thừa hưởng từ nó.

+0

được bỏ phiếu. Câu hỏi tiền thưởng được hỏi năm nay: [[Tại sao IList chung <> không kế thừa IList không chung chung] (http://stackoverflow.com/questions/14559070/why-generic-ilist-does-not-inherit-non- generic-ilist)] –

3
  1. Có, vì List<T> có thể có các thuộc tính và phương pháp để hoàn thành tất cả các giao diện đó. Bạn không biết giao diện người nào đó sẽ khai báo như một tham số hoặc giá trị trả về, do đó, nhiều danh sách giao diện thực hiện, thì nó càng linh hoạt hơn.
  2. Mã của bạn sẽ biên dịch vì upcasting (var a1 = (I1)a;) không thành công khi chạy không biên dịch thời gian. Tôi có thể làm var a1 = (int)a và biên dịch.
+0

nó có vẻ là làm việc ... tôi thêm console.readline để kết thúc và nó có đầu vào của tôi và chấm dứt mà không có bất kỳ lỗi nào – idm

+0

Nó sẽ chạy tốt. Điều gì làm cho bạn nghĩ rằng nó sẽ không? –

5

Giao diện không chung chung là khả năng tương thích ngược. Nếu bạn viết mã bằng cách sử dụng Generics và muốn chuyển danh sách của bạn đến một số mô-đun được viết bằng .NET 1.0 (không có Generics), bạn vẫn muốn điều này thành công. Do đó IList, ICollection, IEnumerable.

2

List<> triển khai tất cả các giao diện đó để hiển thị chức năng được mô tả bởi các giao diện khác nhau. Nó bao gồm các tính năng của Danh sách, Bộ sưu tập và Số lượng chung (với khả năng tương thích ngược với các tương đương không chung)

5

Câu hỏi hay và câu trả lời hay từ Eric Lippert. Câu hỏi này đến với tâm trí của tôi trong quá khứ, và sau đây là sự hiểu biết của tôi, hy vọng nó sẽ giúp bạn.

Giả sử tôi là lập trình viên C# ở hành tinh khác trong vũ trụ, trên hành tinh này, tất cả động vật đều có thể bay.Vì vậy, chương trình của tôi trông giống như:

interface IFlyable 
{ 
    void Fly(); 
} 
interface IAnimal : IFlyable 
{ 
    //something 
} 
interface IBird : IAnimal, IFlyable 
{ 
} 

Vâng, bạn có thể bị nhầm lẫn, vì BirdsAnimals, và tất cả Animals có thể bay, tại sao chúng ta cần phải xác định IFlyable trong giao diện IBird? OK Hãy thay đổi thành:

interface IBird : IAnimal 
{ 
} 

Tôi chắc chắn 100% chương trình hoạt động như trước đây, vậy không có gì thay đổi? IFlyable trong giao diện IBird hoàn toàn vô dụng? Hãy tiếp tục.

Một ngày, công ty của tôi đã bán phần mềm cho một công ty khác trên Trái đất. Nhưng trên trái đất, không phải tất cả động vật đều có thể bay! Vì vậy, tất nhiên, chúng ta cần phải sửa đổi giao diện IAnimal và các lớp thực hiện nó. Sau khi sửa đổi, tôi thấy rằng IBird không chính xác bây giờ vì chim trong trái đất có thể bay! Bây giờ tôi rất tiếc khi xóa IFlyable từ IBird!

+0

Err ... +1 cho Fly –

+0

Ngoại trừ câu trả lời của Eric nói _source code_ thực sự chỉ có tương đương với IBird: IAnimal, và IFlyable chỉ xuất hiện trong MSDN, ILSpy, vv, như bổ sung (suy luận) để giúp bạn. –

+0

Nhưng sau đó bạn phải đi qua tất cả các giao diện khác 'IDog',' ICat', 'IRhinoceros', v.v., có lẽ bạn cũng đã thêm' IFlyable' vào trong vũ trụ mọi động vật bay của bạn, và * remove * IFlyable from chúng. Dù bằng cách nào, bạn có rất nhiều cập nhật để làm. – Blorgbeard

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