16

雖然試圖覆蓋屬性的顯式接口實現類,但我發現一些文檔指出顯式接口成員實現不能被覆蓋,因爲它們不能被修飾符,如virtualabstract。在MSDN上,他們甚至會指定如何通過創建由顯式接口成員實現調用的另一個抽象或虛擬成員來使顯式接口成員實現可用於繼承。目前沒有問題。C#:通過明確指定接口覆蓋屬性

但後來我想:爲什麼有可能在C#只是通過指定接口明確覆蓋任何顯式實現接口成員?

例如,假設我有一個簡單的界面,這樣,具有屬性和方法:

public interface IMyInterface 
{ 
    bool AlwaysFalse { get; } 
    bool IsTrue(bool value); 
} 

並明確實現該接口的類A,並有一個方法Test()它調用自己的接口成員實施。

public class A : IMyInterface 
{ 
    bool IMyInterface.AlwaysFalse 
    { get { return false; } } 

    bool IMyInterface.IsTrue(bool value) 
    { return value; } 

    public bool Test() 
    { return ((IMyInterface)this).AlwaysFalse; } 
} 

正如你所看到的,沒有一個四位成員都是虛擬的或抽象的,所以當我定義一個類B這樣的:

public class B : A 
{ 
    public bool AlwaysFalse 
    { get { return true; } } 

    public bool IsTrue(bool value) 
    { return !value; } 
} 

然後你會期望B投的一個實例A表現得像A。它的確如此:

A a = new A(); 
Console.WriteLine(((IMyInterface)a).AlwaysFalse); // False 
Console.WriteLine(((IMyInterface)a).IsTrue(false)); // False 
Console.WriteLine(a.Test());       // False 
A b = new B(); 
Console.WriteLine(((IMyInterface)b).AlwaysFalse); // False 
Console.WriteLine(((IMyInterface)b).IsTrue(false)); // False 
Console.WriteLine(b.Test());       // False 

現在來抓住。創建一個類C這是B的精確副本,除了在類聲明一兩件事:

public class C : A, IMyInterface 
{ /* ... same as B ... */ } 

現在實例的C,轉換爲A時不會表現得像A但像C

A c = new C(); 
Console.WriteLine(((IMyInterface)c).AlwaysFalse); // True 
Console.WriteLine(((IMyInterface)c).IsTrue(false)); // True 
Console.WriteLine(c.Test());       // True 

即使Test()方法現在調用C中的重寫方法!爲什麼是這樣?

+0

非常有趣的問題! – 2010-09-14 09:00:42

回答

10

這有沒有用顯式接口實現;這只是繼承和接口映射的一般規則的結果:如果類型A提供了IMyInterface的隱式而非明確的實現,您將看到完全相同的結果

  • 類型B繼承自類型A。什麼都沒有被覆蓋。
    B提供自己的AlwaysFalseIsTrue成員,但他們不要實施IMyInterface; IMyInterface的實現由從A繼承的成員提供:當類型B的實例被轉換爲IMyInterface時,它的行爲方式與A類型的實例完全相同,因爲A提供實現該接口的成員。
  • 類型C繼承A類型。再一次,沒有任何東西被覆蓋。
    C提供了自己的AlwaysFalseIsTrue成員,但此時這些成員實施IMyInterface:當C類型的實例被強制轉換爲IMyInterface那麼C成員提供的接口實現,而不是那些A

因爲A型器具IMyInterface明確地,編譯器不警告的BC的部件隱藏的A成員;實際上,由於顯式接口實現,A的這些成員已經被隱藏了。

如果更改輸入A實施IMyInterface隱含而不是明確,則編譯器會警告的BC成員都躲起來,不重寫的A成員和你最好應使用聲明的時候new修改BC中的成員。

以下是語言規範中的一些相關位。 (第20.4.2和在ECMA-334 spec 20.4.4;部分13.4.4和在Microsoft C#4 spec 13.4.6。)

20.4.2接口映射

特定 接口的實現構件I.M,其中I是 ,其中構件M 被聲明的接口,通過確定 檢查每個類或結構S, 開始C並且重複 每個連續的基類C, 直到找到匹配。

20.4.4接口重新實現

繼承的接口 實現允許通過 重新實施的接口,其包括在它的基類列表的類。 A 接口 的重新實現遵循完全相同的接口 映射規則作爲接口的初始 實現。因此, 繼承的接口映射沒有 影響接口 映射爲接口重新實現而建立的 。

+0

所以你說這是合乎邏輯的,並且期望任何人都可以爲任何顯式接口成員提供一個新的實現,並且最重要的是,原始類將調用該新成員(隱藏原始實現的那個成員)而沒有任何重載,虛擬或抽象方法? – Virtlink 2010-09-14 09:06:58

+0

正是因爲你不重寫,你看到了這種行爲:例如,類型'B'提供了'IsTrue'的* new *實現,它*不會*重寫'IsTrue'方法的類型' A'。類型'B'只從'A'繼承它的'IMyInterface'的實現。它是實現'IMyInterface'的'A.IsTrue'方法,因此當您將'B'的實例轉換爲'IMyInterface'時,它就是使用的'A.IsTrue'方法。 [...] – LukeH 2010-09-14 09:15:48

+0

類型'C'還提供了一個'IsTrue'的*新*實現,它*不會覆蓋'A'類型的'IsTrue'方法。但是輸入'C'直接實現'IMyInterface'。按照語言規範中的規則,將'C'的實例投射到'IMyInterface'時,它將使用'C.IsTrue'方法。 – LukeH 2010-09-14 09:17:31