2010-08-13 89 views
13

可以將界面中的方法聲明標記爲「」,但是它有任何「技術」意義,還是僅僅是明確聲明聲明不能覆蓋前一個聲明的方法?隱藏(使用「新」修飾符)接口方法聲明的目的是什麼?

例如:

interface II1 
{ 
    new void F(); 
} 

interface II2 : II1 
{ 
    new void F(); 
} 

有效(C#4.0編譯器不抱怨),但不會出現來自不同的:事先的任何信息

interface II1 
{ 
    void F(); 
} 

interface II2 : II1 
{ 
    void F(); 
} 

感謝。

編輯:你知道一個隱藏在界面中的場景嗎?

編輯:根據此鏈接:Is method hiding ever a good idea(感謝斯科特), 最常見的場景似乎是協變返回類型的仿真。

回答

8

第二個例子發出以下編譯器警告:

'II2.F()' 揣繼承的成員 'II1.F()'。如果打算使用 隱藏,則使用新關鍵字。

我想說使用new關鍵字的區別就是:顯示意圖。

+0

感謝您的回答。 所以它不會影響接口的使用方式? 我會編輯最初的帖子來澄清這一點。 – Pragmateek 2010-08-13 12:37:33

+1

@Serious:不,唯一的區別是編譯器警告。我建議使用'new'關鍵字來清楚。那麼,不,我實際上會建議不要隱藏方法,因爲我覺得它很容易混淆,但是如果*需要*,使用'new'關鍵字。 – 2010-08-13 12:46:53

+0

你說得對,我也沒有看到任何應該需要的有趣用例。 – Pragmateek 2010-08-13 13:04:18

6

這兩者是非常不同的。通過使用'新'你正在創建一個新的繼承鏈。這意味着II2的任何實現都需要實現F()這兩個版本,並且最終調用的實際版本將取決於引用的類型。

考慮以下三種實現方式:

class A1 : II1 
    { 
     public void F() 
     { 
      // realizes II1.F() 
     } 
    } 

    class A2 : II2 
    { 
     void II1.F() 
     { 
      // realizes II1.F() 
     } 

     void II2.F() 
     { 
      // realizes II2.F() 
     } 
    } 

    class A3 : II2 
    { 
     public void F() 
     { 
      // realizes II1.F() 
     } 

     void II2.F() 
     { 
      // realizes II2.F() 
     } 
    } 

如果你有A2一個參考,你將無法調用F()任一版本而無需首先鑄造II1II2

A2 a2 = new A2(); 
a2.F(); // invalid as both are explicitly implemented 
((II1) a2).F(); // calls the II1 implementation 
((II2) a2).F(); // calls the II2 implementation 

如果你有A3一個參考,你就可以直接調用II1版本,因爲它是一個隱含implentation:

A3 a3 = new A3(); 
a3.F(); // calls the II1 implementation 
((II2) a3).F(); // calls the II2 implementation 
+2

請注意,問題中給出的兩個示例之間的編譯代碼沒有差異。你解釋的區別在於方法隱藏的效果,但它會發生,不管是否使用'new'關鍵字。 – 2010-08-13 12:57:51

+0

感謝這些很棒的例子。 – Pragmateek 2010-08-13 13:02:30

+0

@Fredrik:哎呀,謝謝。出於某種原因,我完全忽略了這一點。你確實是正確的,警告是唯一的區別。 – 2010-08-16 09:57:34

4

我知道一個很好的利用了這一點:你這樣做來聲明從另一個COM接口派生的COM接口。這在magazine article中被觸及。

Fwiw,作者完全錯誤地標識了問題的根源,它與「繼承稅」無關。 COM只是利用了典型的C++編譯器實現多重繼承的方式。每個基類有一個v表,就是COM需要的。 CLR不這樣做,它不支持MI,並且只有一個V表。接口方法被合併到基類的v表中。

+0

感謝這個好例子。 – Pragmateek 2010-08-13 14:41:42

1

新的修飾符非常簡單,它只是抑制隱藏方法時創建的編譯器警告。如果用於不隱藏其他方法的方法,則會生成警告。

The C# Language Specification 3.0

10.3.4 new修飾符 一類成員聲明允許聲明一個部件用相同的名稱或簽名一個繼承的成員。發生這種情況時,派生類成員被稱爲隱藏基類成員。隱藏繼承的成員不被視爲錯誤,但它確實會導致編譯器發出警告。爲了抑制警告,派生類成員的聲明可以包含一個新的修飾符,以指示派生成員用於隱藏基本成員。本主題將在第3.7.1.2節中進一步討論。 如果在不隱藏繼承成員的聲明中包含新修飾符,則會發出警告。該警告通過刪除新的修改器而被抑制。

1

弗雷德裏克答案後的一個例子。我想要一個接口來表示通過id獲取實體的方法。然後,我想要一個WCF服務和一些其他標準存儲庫能夠用作該接口。爲了玩WCF,我需要用屬性來修飾我的界面。從意圖的角度來看,我不想用WCF的東西來裝飾我的界面,因爲它不會僅通過WCF使用,所以我需要使用新的界面。這裏的代碼:

public class Dog 
{ 
    public int Id { get; set; } 
} 

public interface IGetById<TEntity> 
{ 
    TEntity GetById(int id); 
} 

public interface IDogRepository : IGetById<Dog> { } 

public class DogRepository : IDogRepository 
{ 
    public Dog GetById(int id) 
    { 
     throw new NotImplementedException(); 
    } 
} 

[ServiceContract] 
public interface IWcfService : IGetById<Dog> 
{ 
    [OperationContract(Name="GetDogById")] 
    new Dog GetById(int id); 
} 
+0

有趣的是,它來自一個真實的用例嗎? – Pragmateek 2013-12-01 22:47:47

+0

是的,我的所有存儲庫檢索方法都是在一個接口(每個方法一個)後面抽象出來的。然後,任何需要支持某種檢索的對象都可以實現該接口。其中一些是WCF服務,這些服務產生了上述代碼。 – greyalien007 2013-12-02 16:23:53

1

我幾乎在我的每個接口中使用它。看這裏:

interface ICreature 
{ 
    /// <summary> 
    ///  Creature's age 
    /// </summary> 
    /// <returns> 
    ///  From 0 to int.Max 
    /// </returns> 
    int GetAge(); 
} 

interface IHuman : ICreature 
{ 
    /// <summary> 
    ///  Human's age 
    /// </summary> 
    /// <returns> 
    ///  From 0 to 999 
    /// </returns> 
    new int GetAge(); 
} 

有一個繼承成員的要求較低,但義務較大是絕對正常的。不違反LSP。但如果是這樣,在哪裏記錄新的合同?

+1

不確定在這種情況下創建'new'方法是最合適的方法。對於具有不同年齡範圍的應該由實現內部管理的內容來說,每個實現都記錄它們的不變量。如果我繼續你的榜樣,並不是所有的人都有相同的範圍:族長的確有一個0-999的範圍,但從那時起範圍更多0-149。正如你所說,這種契約不能用語言或運行時本身來描述,但是你可以使用像System.ComponentModel.DataAnnotations這樣的元數據。RangeAttribute'。 – Pragmateek 2014-05-18 09:57:32

+1

這不是關於範圍,而是關於要求和義務。範圍只是最簡單的要求示例。在實現中管理這些req/obl會導致重複/不可重用的代碼。在這樣一個簡單的例子中並不明顯,但在具有深層次結構的大型項目中是不可避免的。關於不是所有的人都有相同的範圍 - 這取決於你的域名。在我的抽象世界 - 這是真的。在你的 - 做你自己的抽象;) – astef 2014-05-18 14:20:41

+0

是的但不是所有的要求都應該用這種方式表達。再舉一個例子,如果我有一個帶'getNumberOfWheels'方法的'Vehicle'接口,沒有理由重新定義一個新方法,因爲一輛自行車有2個車和一個汽車4.重新定義一個新方法是非常重要的,如果他們做了一些不同的功能。 – Pragmateek 2014-05-18 15:23:44

相關問題