首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

C# new、virtual、override 關鍵字深度解析:隱藏、覆寫與多型機制

     編輯:Java 相關知識

本文透過四個版本的範例程式,層層剖析 C# 中 new、virtual、override 的行為差異,並解釋早期繫結與晚期繫結的底層機制。

版本一:無修飾詞 (預設隱藏)

public class Father { public void Run0() { Console.WriteLine("Father.Run0"); } }
public class Son : Father { public void Run0() { Console.WriteLine("Son.Run0"); } }

Father[] list = new Father[2];
list[0] = new Father();
list[1] = new Son();
list[0].Run0(); // Father.Run0
list[1].Run0(); // Father.Run0  ← 注意!非 Son.Run0

結果:皆輸出 Father.Run0。
編譯警告:Son.Run0() 隱藏繼承成員 Father.Run0(),建議加 new 關鍵字。
機制:早期繫結 — 編譯期依參考型別 (Father) 決定呼叫基類方法。

版本二:子類加 new (顯式隱藏)

public class Son : Father { public new void Run1() { Console.WriteLine("Son.Run1"); } }

結果:同版本一,皆輸出 Father.Run1。
結論:new 僅消除警告,明確表達「隱藏基類方法」意圖,不改變呼叫行為。C# 隱式為同名同參方法加 new。

版本三:基類加 virtual,子類無修飾詞

public class Father { public virtual void Run2() { Console.WriteLine("Father.Run2"); } }
public class Son : Father { public void Run2() { Console.WriteLine("Son.Run2"); } }

結果:仍輸出兩次 Father.Run2。
警告:Son.Run2() 將隱藏繼承成員,若要覆寫請加 override,否則加 new。
關鍵:virtual 僅表示「允許被覆寫」,若子類未加 override,仍視為隱藏,早期繫結生效。

版本四:基類 virtual + 子類 override (真正多型)

public class Son : Father { public override void Run3() { Console.WriteLine("Son.Run3"); } }

結果:

Father.Run3
Son.Run3   ← 參考型別 Father,實際型別 Son,呼叫 Son 方法!

機制:晚期繫結 — 執行期依實際物件型別 決定呼叫方法。

核心概念對照表

關鍵字/組合繫結時機行為適用場景
無修飾詞 / new編譯期 (早期繫結)隱藏基類方法,參考型別決定呼叫非多型設計、刻意切斷繼承鏈
virtual (基類) + 無修飾詞 (子類)編譯期 (早期繫結)子類隱藏基類虛擬方法,非多型幾乎無合理用途,通常為錯誤
virtual + override執行期 (晚期繫結)真正多型,實際型別決定呼叫標準多型設計、擴充點

為什麼 C# 預設非虛擬?

  • 效能:非虛擬方法無需型別檢查,直接呼叫,效率較高
  • 明確性:強制開發者以 virtual/override 明確宣告多型意圖,避免意外覆寫
  • 版本控管:基類庫作者可安全新增非虛擬方法,不破壞衍生類

對比 Java:所有方法預設為虛擬,執行期自動檢測實際型別,帶來微小效能成本但語法更簡潔。

實務建議

  1. 預設不加 virtual,除非確定需提供擴充點
  2. 需多型時,基類 必須 virtual,子類 必須 override
  3. 刻意隱藏基類方法時,子類加 new 消除警告並文件化意圖
  4. 避免 virtual + 子類無修飾詞 的組合 (極易誤導)
  5. 使用 sealed override 防止進一步覆寫

總結

new = 隱藏 (早期繫結);virtual = 允許覆寫;override = 實作覆寫 (晚期繫結)。三者缺一不可構成真正多型。理解早/晚期繫結差異,是掌握 C# 物件導向精髓的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved