2010-03-04 39 views
37

Tôi hiểu rằng các giao diện là các hợp đồng và bất kỳ thay đổi nào (thậm chí bổ sung) sẽ phá vỡ bất kỳ mã phụ thuộc nào. Tuy nhiên, tôi có thể thề rằng tôi đã đọc một cái gì đó trong khi một trong những phiên bản .NET gần đây (3, 3.5 ??) đã thêm một thuộc tính mới có thể được áp dụng cho các thành viên giao diện mới. Thuộc tính này cho phép phiên bản và/hoặc tạo thành viên tùy chọn. Nó sẽ giống như sau:C# Giao diện với các phương thức tùy chọn

interface ITest 
{ 
    void MethodOne(); 

    [InterfaceVersion(2)] 
    void MethodTwo(); 
} 

Tôi đã xem xét cao và thấp cho điều này nhưng dường như không thể tìm thấy nó. Tôi tự hỏi liệu tôi có đơn giản hiểu lầm bất cứ điều gì tôi nghĩ rằng tôi đọc và không có điều đó. Có ai có hiểu biết gì không?

+0

Ok, phiếu bầu áp đảo đã là "không, nhưng ...". Tôi sẽ đổ lỗi cho điều này vào một ngày dài và tôi cảm thấy mệt mỏi. :) –

+0

Tôi đã suy nghĩ một chút về cách thức/nếu điều này có thể hoạt động. Trừ khi tôi bị nhầm lẫn, chỉ có trình biên dịch người tiêu dùng mới phải "hỗ trợ" điều này. Trình biên dịch giao diện sẽ đơn giản thêm thuộc tính vào assembly, trong khi trình biên dịch người tiêu dùng sẽ là trình biên dịch để thực thi các triển khai. Nó sẽ gần như (mặc dù không phải về mặt kỹ thuật) là "mã đường" để ngăn chặn ITest1, ITest2, vv –

+0

Người tiêu dùng sẽ phải chỉ định phiên bản nó đang sử dụng, nhưng sau đó bạn có một số khó khăn. Điều gì sẽ xảy ra nếu mã của bạn sử dụng cả hai phiên bản? Vì vậy, bạn có Class1: [InterfaceVersion (1)] ITest và Class2: [InterfaceVersion (2)] ITest. Sau đó, bạn làm "nếu (Class2 là ITest)" ... nhưng cái nào? Nó giống như khi bạn sử dụng thuộc tính ở một nơi, bạn phải sử dụng nó ở mọi nơi. Trình biên dịch sẽ phải theo dõi tất cả điều này và buộc nó vào bạn. Nó có thể sẽ dễ dàng hơn khi thực hiện ITest1, ITest2 ... Vì vậy, thuộc tính "tiện lợi" mới này có thể thực sự làm cho nó tồi tệ hơn. :) –

Trả lời

41

Bạn nên tạo hai giao diện:

interface ITest 
{ 
    void MethodOne(); 
} 

interface ITest2 : ITest 
{ 
    void MethodTwo(); 
} 

Điều này cũng sẽ làm cho nó rõ ràng mà chức năng đòi hỏi phiên bản giao diện của bạn, do đó bạn không cần phải kiểm tra xem các lớp thực hiện giao diện được thực hiện chỉ một hoặc cả hai phương pháp.

+0

Tôi đã thấy điều đó được thực hiện trong COM ngày. Sau một vài năm có thể trở thành một danh sách dài, nhưng lúc đó bạn có thể muốn thiết kế lại API của mình. –

+0

Nhưng điều này đánh bại mục đích có quyền hợp đồng chung. Tôi có nghĩa là tôi sẽ có thể vượt qua một đối tượng thực hiện một trong hai giao diện (ITest, ITest2) đến một chức năng mà không có diễn viên xấu xí. Tôi nghĩ rằng có một giao diện duy nhất (mà circumscribes tất cả các phương pháp) trong trường hợp này ITest2 và những người muốn thực hiện chỉ một vài chức năng sẽ chỉ cần ném NotSupportedException cho phần còn lại như mô tả ở đây https://stackoverflow.com/a/ 4566711/3921536 – router

+0

NotSupportedException/NotImplementedException về mặt giao diện phải là giải pháp sao lưu cuối cùng mà bạn chuyển sang, khi không có phương tiện khác để cấu trúc mã hoạt động cho bạn. Nó tương đương với câu nói "Chắc chắn, tôi có thể khiến bạn làm việc vào ngày mai" và sau đó để bật lên với một chiếc xe đạp đi "Fooled bạn!". Một giao diện nên được coi là một hợp đồng, nếu bạn nói bạn hỗ trợ nó, bạn ** thực sự ** nên hỗ trợ nó. Có một số trường hợp các nhà phát triển đã biện minh bằng NotSupportedException nhưng điều này chỉ nên được sử dụng như một phương sách cuối cùng. –

4

Tôi biết không có thuộc tính nào cho phép triển khai giao diện được triển khai một phần. Bạn có thể làm việc xung quanh này sử dụng một lớp trừu tượng, tuy nhiên:

public abstract class Test 
{ 
    public abstract void MethodOne(); 
    public virtual void MethodTwo() { } 
} 

này sẽ cho phép người sử dụng để quyết định có hay không họ muốn ghi đè MethodTwo khi kế thừa từ Test, trong khi buộc trọng của MethodOne.

+0

Đôi khi, giải pháp này, trong khi chắc chắn không sai, có thể phá vỡ mã hiện có cho việc thiếu nhiều thừa kế. –

+0

Vâng, điều này tương tự như những gì Kilhoffer nói. Tôi nhận thức được điều này nhưng tôi thường tránh các lớp cơ sở trừ khi lớp mới của tôi "là một" BaseClass (so với "có một" ISomething). –

5

Không có thuộc tính nào trong khuôn khổ .NET.

+1

Tôi đặc biệt xem xét từng thuộc tính tại http://msdn.microsoft.com/en-us/library/aa311259%28VS.71%29.aspx và không tìm thấy một thuộc tính nổi bật. Tôi sẽ phải đồng ý với bạn ngay bây giờ. –

10

Tôi không thấy thuộc tính như vậy, nhưng tôi đoán là có thể. This article trên MSDN mô tả phiên bản thông qua việc sử dụng các từ khóa overridesnew.

Tóm lại, C# được trang bị các tính năng ngôn ngữ cho phép các lớp dẫn xuất phát triển và vẫn duy trì khả năng tương thích. Ví dụ này cho thấy mối quan hệ hoàn toàn dựa trên cơ sở, nhưng cơ sở thực sự sẽ thực hiện giao diện bạn cần cho phiên bản. Có một giao diện yêu cầu giao diện khác (phiên bản trước) cùng với phương pháp này khá hữu ích.

Ví dụ về việc tạo ra một giao diện mà đòi hỏi khác:

public interface IMyInterface 
{ 
    void FirstMethod(); 
} 

public interface IMySecondInterface : IMyInterface 
{ 
    void SecondMethod(); 
} 

Ví dụ về sử dụng thừa kế để duy trì khả năng tương thích:

public class MyBase 
{ 
    public virtual string Meth1() 
    { 
     return "MyBase-Meth1"; 
    } 
    public virtual string Meth2() 
    { 
     return "MyBase-Meth2"; 
    } 
    public virtual string Meth3() 
    { 
     return "MyBase-Meth3"; 
    } 
} 

class MyDerived : MyBase 
{ 
    // Overrides the virtual method Meth1 using the override keyword: 
    public override string Meth1() 
    { 
     return "MyDerived-Meth1"; 
    } 
    // Explicitly hide the virtual method Meth2 using the new 
    // keyword: 
    public new string Meth2() 
    { 
     return "MyDerived-Meth2"; 
    } 
    // Because no keyword is specified in the following declaration 
    // a warning will be issued to alert the programmer that 
    // the method hides the inherited member MyBase.Meth3(): 
    public string Meth3() 
    { 
     return "MyDerived-Meth3"; 
    } 

    public static void Main() 
    { 
     MyDerived mD = new MyDerived(); 
     MyBase mB = (MyBase) mD; 

     System.Console.WriteLine(mB.Meth1()); 
     System.Console.WriteLine(mB.Meth2()); 
     System.Console.WriteLine(mB.Meth3()); 
    } 
} 
+0

Tôi đã nhận thức được phương pháp này, nhưng nó không sử dụng giao diện. Tôi đã nhận được rất nhiều câu trả lời nói "không", rằng tôi phải hiểu lầm nó. –

+0

Lớp cơ sở có thể triển khai giao diện gốc – Kilhoffer

+0

Đó là sự thật. Tuy nhiên, tôi nghĩ rằng nó có thể là một chút bối rối ý định của bạn là gì, ngay cả khi nó hoàn toàn hợp lệ. Nó vẫn có thể là cách tốt nhất trong một số trường hợp (không có giải pháp "viên đạn bạc" luôn là giải pháp tốt nhất). –

0

Bạn có thể nghĩ đến việc partial methods (MSDN).

+0

Không, nhưng tôi thậm chí không biết bạn có thể làm điều đó. Các lớp một phần được sử dụng trên tất cả (thiết kế so với mã do người dùng tạo).Tôi đã học về rất nhiều "trường hợp/tính năng cạnh" trong C# /. NET đọc qua diễn đàn này! –

2

Gần đây tôi đang ở trong tình huống mà sự thiếu quyết của đa kế thừa cấm tôi để chuyển đổi một giao diện hiện có thành một lớp trừu tượng, và tìm thấy bản thân mình với một giải pháp mở rộng:

interface IFoo { 
      int RowCount(); 
    } 

    static class _FooExtensions { 
      public static bool HasAnyRows (this IFoo foo) { 
        return foo.RowCount() > 0; 
      } 
    } 

Bằng cách đó bạn có thể cung cấp một phiên bản mặc định trong trường hợp phương pháp trừu tượng của bạn có thể được xác định theo các chức năng khác.

+0

Phải, đây sẽ giống như một lớp trợ giúp. Lúc đầu, tôi nghĩ rằng bạn đã đề cập đến các phương pháp mở rộng mà trong một số trường hợp sẽ làm việc tốt (LINQ, vv). Có một điều nổi bật, "Cái quái gì này" làm cái gì? Tôi đã sử dụng điều này để truy cập các thành viên cá thể bao gồm nhảy từ một nhà xây dựng này sang một nhà xây dựng khác, nhưng tôi chưa bao giờ thấy nó được sử dụng theo cách này. –

+0

@Nelson: đó là một phương pháp mở rộng. Chúng không chỉ được sử dụng cho LINQ. –

+0

Rất tiếc, xấu của tôi. Tôi đã sử dụng một phương pháp mở rộng một hoặc hai lần, nhưng không nhớ cú pháp đầy đủ. Tôi hiểu nó có nhiều công dụng. Nó vẫn là một lớp "helper" về mặt kỹ thuật, nhưng vẫn duy trì các khái niệm OOP mà không thực sự thực hiện một giao diện hoặc bắt nguồn từ một lớp. Rất tiện dụng đôi khi. –

1

Bạn có thể đọc một cái gì đó giống như

interface ITest 
{ 
    void MethodOne(); 

    [InterfaceVersion(2)] 
    void MethodTwo(); 
} 



[AttributeUsage(AttributeTargets.All)] 
public class InterfaceVersion : System.Attribute 
{ 
    public readonly int N; 

    public InterfaceVersion(int n) 
    { 
     this.N = n; 
    } 
} 

Nhưng tôi không nghĩ rằng có thể làm cho việc thực hiện các MethodTwo tùy chọn.

EDIT:

Tôi vừa phát hiện ra bằng cách chạy mã mà nó thực sự không làm cho việc thực hiện các MethodTwo tùy chọn.

+0

Phải, trình biên dịch sẽ không cho phép nó là tùy chọn, trừ khi Microsoft được tích hợp cụ thể (như [Obsolete()], v.v.). Thậm chí sau đó tôi không chắc chắn làm thế nào mà sẽ làm việc và có thể không thể làm. –

+0

Để chỉnh sửa của bạn: vâng, nó chỉ đơn giản là thuộc tính tùy chỉnh xảy ra có "Phiên bản" trong tên. Nó cũng có thể được gọi là "InterfaceNumber" và có chính xác cùng một hiệu ứng. –

6

Bạn có thể nghĩ đến tính năng "không pia" mới trong C# 4 không? Tức là, chúng tôi cho phép bạn "liên kết trong" chỉ các phần của giao diện bạn thực sự sử dụng từ PIA và sau đó bạn có thể bỏ qua việc chuyển PIA cho khách hàng của mình. Nếu bạn sau đó làm điều này nhiều lần trong một số hội đồng khác nhau, CLR thực hiện công việc tìm ra rằng tất cả các giao diện một phần liên kết đó là hợp lý cùng một loại, và hợp nhất chúng. Bằng cách đó, bạn có thể truyền các đối tượng thực hiện từng giao diện của giao diện từ một assembly này đến assembly khác và tất cả đều hoạt động. Tuy nhiên, giao diện gốc mà giao diện "không có pia" được tạo từ phải giống nhau.

+0

Tôi không nghĩ rằng đây là nó, nhưng âm thanh thú vị. Bạn có cơ hội nào có liên kết đến một số thông tin khác (ví dụ, v.v.) không? –

+1

@Nelson: http://blogs.msdn.com/samng/archive/2010/01/24/the-pain-of-deploying-primary-interop-assemblies.aspx –

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