針對 Allen Lee 關於基類與接口混合繼承聲明的問題,本文通過代碼實驗與 IL 反編譯,深入剖析 C# 接口映射的實現機制。
定義一個介面 接口、基類 基類 實現該介面,再建立兩個衍生類:沒有接口的繼承(僅繼承基類)與 有接口的繼承(繼承基類並再次實現介面)。
interface 接口 { void 方法(); }
class 基類 : 接口 {
public void 方法() { Console.WriteLine("基類方法"); }
void 接口.方法() { Console.WriteLine("介面專用實現"); }
}
class 沒有接口的繼承 : 基類 {
public new void 方法() { Console.WriteLine("用 new 隱藏"); }
}
class 有接口的繼承 : 基類, 接口 {
void 接口.方法() { Console.WriteLine("介面重映射"); }
}

可見每個「方法()」實際上都是不同的實體。
.method public hidebysig instance void 方法() cil managed {
.maxstack 1
L_0000: ldstr "我只能使用 new 來屏蔽它..."
L_0005: call void [mscorlib]System.Console::WriteLine(string)
L_000a: ret
}
.method private hidebysig newslot virtual final instance void Test.接口.方法() cil managed {
.override Test.接口::方法
.maxstack 1
L_0000: ldstr "這就叫做接口的重映射..."
L_0005: call void [mscorlib]System.Console::WriteLine(string)
L_000a: ret
}
關鍵差異:.override Test.接口::方法 指令顯式指定覆寫的是介面方法槽,而非基類的虛擬方法。
| 訪問方式 | 無介面繼承 | 有介面繼承 |
|---|---|---|
| 基類引用調用 | callvirt 基類::方法() | callvirt 基類::方法() |
| 介面引用調用 | callvirt 介面::方法() | callvirt 介面::方法() |
雖然 IL 看似相同,但運行時會根據物件實際類型分派到不同槽位。
有人認為 VB 難以實現,實則不然。介面本質上是特殊抽象類,VB 通過 Implements 關鍵字同樣可實現顯式介面映射:
Friend Class 有接口的繼承
Inherits 基類
Implements 接口
Private Sub Test_接口_方法() Implements 接口.方法
Console.WriteLine("VB 介面映射")
End Sub
End Class
介面映射與 new 隱藏同屬技巧性手段,雖能解決特定問題,但:
介面的價值在於提供橫向關係描述,彌補單繼承的縱向限制,應作為架構邊界而非實現細節的替換工具。