2009-01-03 35 views
9

Gần đây tôi đã thực hiện một lớp học như:Tại sao triển khai giao diện rõ ràng?

class TestClass : IDisposable 
{ 
    RegistryKey m_key; 
    public TestClass() 
    { 
     m_key = Registry.CurrentUser.OpenSubKey("Software", false); 
    } 

    public void Dispose() 
    { 
     // m_key.Dispose(); 
     IDisposable disp = m_key; 
     disp.Dispose(); 
    } 
} 

Nếu tôi bỏ ghi chú các cuộc gọi trực tiếp để Vứt bỏ, tôi nhận được lỗi CS0117 (" 'Microsoft.Win32.RegistryKey' không chứa một định nghĩa cho 'Vứt bỏ"). Một số Googling dẫn tôi đến this thread, nơi tôi đã học được những gì đang diễn ra, vì vậy bây giờ tôi đã hiểu được cơ chế của nó. Tài liệu MSDN cho thấy tác giả muốn tôi gọi Close() thay vì Dispose(), nhưng không giải thích tại sao.

Mục đích của mẫu này (tôi nghĩ rằng tôi đã nhìn thấy nó trong các lớp IO) là gì? Trong thực tế rằng đây là một quyết định có chủ ý của tác giả lớp, làm thế nào xấu là mã ở trên (các cuộc gọi để Vứt bỏ thông qua giao diện IDisposable)? Nó không thể quá tệ - sau khi tất cả, đó là những gì sẽ xảy ra trong một tuyên bố sử dụng, phải không?

[chỉnh sửa: 1) thay đổi tiêu đề từ "phi công" để "rõ ràng" 2) loại bỏ việc thực hiện rõ ràng từ mã của tôi, vô tình còn lại trong từ thử nghiệm]

+1

Bạn có thể sử dụng '(m_key như IDisposable) .Dispose();' để viết tắt. –

Trả lời

15

này được gọi là thực hiện giao diện rõ ràng. Trong ví dụ của bạn vì bạn định nghĩa phương thức Dispose() là "void IDisposable.Dispose()", bạn đang triển khai một cách rõ ràng giao diện IDisposable.

Điều này thường được thực hiện để tránh va chạm. Nếu Microsoft đã từng muốn thêm một phương thức Dispose() khác đã làm một cái gì đó khác cho RegistryKey, họ sẽ không thể trừ khi họ sử dụng việc thực hiện rõ ràng giao diện đó.

Điều này được thực hiện thường xuyên với giao diện IE2umerchung của IEnumerable. Nó đòi hỏi bạn cũng phải thực hiện giao diện không chung chung IEnumerable. Các thành viên duy nhất trong hai giao diện này là GetEnumerator, với một generic là hữu ích hơn, vì vậy nó thường được thực hiện như thế này:

public clas SomeClass : IEnumerable<SomeOtherClass> 
{ 
    public IEnumerator<SomeOtherClass> GetEnumerator() 
    { 
     ... 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Bằng cách này khi bạn gọi một đối tượng của phương pháp GetEnumator SomeClass, nó gọi là phiên bản generic, vì một cái khác được triển khai một cách rõ ràng, cho phép chúng ta có được các generics mạnh mẽ cho phép.

Xem trang 166-169 của Lập trình C# bởi Jesse Liberty (Tôi có ấn bản thứ tư).

-2

Hầu hết mọi người không đồng ý với tôi, nhưng Tôi thích sử dụng triển khai giao diện rõ ràng cho tất cả giao diện. Tôi muốn nói rõ liệu tôi đang viết một phương thức để được gọi trên đối tượng của tôi hay trên giao diện của tôi.

Đây là đau đớn nếu bạn có một tham chiếu đến đối tượng và muốn gọi một phương thức giao diện (như ví dụ trên), nhưng tôi giảm thiểu nó bằng cách viết:

class C : IDisposable 
{ 
    public IDisposable IDisposable { get { return this; } } 
    void IDisposable.Dispose() { } 
} 

có nghĩa là gọi phương thức trên C trông giống như:

C c = ... 
c.IDisposable.Dispose(); 

Trình biên dịch phân tích này là "gọi IDisposable tài sản trên C, sau đó gọi phương thức Dispose() vào kết quả" nhưng tôi đọc nó như là "gọi phương thức IDisposable.Dispose() trên C" w hich có vẻ tự nhiên ở đây.

Cách tiếp cận này có thể trở nên xấu xí khi sử dụng giao diện chung.

+0

Tương tự ở đây trên giao diện rõ ràng. Generics đôi khi rất khó khăn. – JaredPar

+32

Eek - việc sử dụng giao diện rõ ràng không chỉ gây khó khăn hơn cho người gọi mà không có cách giải quyết như bạn ở trên, nhưng nó cũng làm hỏng thừa kế - bạn gặp phải các tình huống khó khăn nếu một lớp dẫn xuất muốn ghi đè lên một phương thức giao diện. Tôi tránh nó khi tôi có thể. –

+0

Các lớp kế thừa thực hiện giao diện nên làm như vậy với các thành viên công cộng ảo (trong trường hợp các thành viên có thể truy cập như thành viên nhóm) hoặc với các thành viên được bảo vệ ảo có tên là dạng sửa đổi của tên thành viên giao diện hoặc chữ ký. VB.NET cho phép sau này được thực hiện trực tiếp 'Protected Overridable Sub Foo_Impl() Thực hiện IInterface.Foo' nhưng trong C# tốt nhất có thể làm là xác định một phương pháp ảo được bảo vệ để giữ" ruột "của giao diện, và có giao diện việc thực hiện không làm gì ngoài việc chuỗi đó. – supercat

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