前言
virtual 與 override 是 C# 實現執行期多型的核心關鍵字。本文詳細解析虛擬方法語法規則、與非虛擬方法行為差異、new 隱藏 vs override 覆寫、編譯期靜態綁定 vs 執行期動態綁定。
一、虛擬方法語法規則
- 基類方法加
virtual 修飾符
- 衍生類用
override 覆寫
- 限制:
virtual 不能與 static、abstract、override 共存
- 限制:
override 不能與 new、static、virtual 共存
- 存取修飾符必須一致(或更寬鬆)
二、虛擬 vs 非虛擬:核心行為差異
| 特性 | 非虛擬方法 | 虛擬方法 |
| 呼叫綁定時機 | 編譯期(靜態綁定) | 執行期(動態綁定) |
| 衍生類改變行為 | 用 new 隱藏 | 用 override 覆寫 |
| 透過基類參考呼叫 | 永遠呼叫基類實作 | 呼叫衍生類實際型別的實作 |
| 效能 | 略快(直接呼叫) | 虛擬表查找(極微小開銷) |
三、程式碼範例:虛擬與非虛擬並存
using System;
class A
{
public void F() { Console.WriteLine("A.F"); } // 非虛擬
public virtual void G() { Console.WriteLine("A.G"); } // 虛擬
}
class B : A
{
public new void F() { Console.WriteLine("B.F"); } // 隱藏基類 F
public override void G() { Console.WriteLine("B.G"); } // 覆寫基類 G
}
class Test
{
static void Main()
{
B b = new B();
A a = b; // 基類參考指向衍生類實例
a.F(); // 輸出:A.F (靜態綁定,a 編譯期型別為 A)
b.F(); // 輸出:B.F (靜態綁定,b 編譯期型別為 B)
a.G(); // 輸出:B.G (動態綁定,實際型別為 B)
b.G(); // 輸出:B.G (動態綁定)
}
}
輸出結果
A.F
B.F
B.G
B.G
關鍵解析
a.F():編譯期型別 A,非虛擬方法 → 靜態綁定 A.F
b.F():編譯期型別 B,new 隱藏 → 靜態綁定 B.F
a.G():編譯期型別 A,但 執行期型別為 B,虛擬方法 → 動態綁定 B.G
- 這就是多型:相同呼叫語法,依實際物件型別執行不同實作
四、new 隱藏 vs override 覆寫:深度比較
| 特性 | new 隱藏 | override 覆寫 |
| 基類方法需求 | 無需 virtual | 必須 virtual/abstract/override |
| 多型行為 | 無(靜態綁定) | 有(動態綁定) |
| 基類參考呼叫 | 呼叫基類方法 | 呼叫衍生類方法 |
| 編譯器警告 | 若未加 new 會警告 CS0108 | 無警告(正確覆寫) |
| 適用場景 | 非虛擬方法、第三方庫、刻意中斷多型 | 設計為可擴充的虛擬方法 |
五、編譯期 vs 執行期綁定圖解
// 編譯期:靜態型別決定
A a = new B(); // a 的靜態型別 = A,動態型別 = B
// 非虛擬:編譯期綁定
a.F(); // 看靜態型別 A → A.F
// 虛擬:執行期綁定
a.G(); // 看動態型別 B → B.G (透過虛擬表 vtable 查找)
虛擬表機制簡述
- 每個含虛擬方法的類別有一個 虛擬方法表
- 表中儲存各虛擬方法的實際進入點位址
- 每個實例物件內部包含指向其類別虛擬表的指標(
__vptr)
- 呼叫虛擬方法時:
obj->vptr[vtable_index] 間接跳轉
六、最佳實務建議
- 預設非虛擬:除非明確設計為可擴充,否則不加
virtual
- 覆寫必加
override:編譯器驗證簽章一致,防止拼錯
- 刻意隱藏加
new:消除警告,明確語意
- 密封類別用
sealed:禁止繼承,間接禁止覆寫
- 密封方法用
sealed override:允許繼承類別但禁止進一步覆寫該方法
七、總結
虛擬方法是 C# 執行期多型的基石:
virtual 宣告「此方法可被覆寫」
override 實作「衍生類的具體行為」
- 動態綁定:透過基類參考呼叫虛擬方法,實際執行衍生類實作
new 僅是編譯期隱藏,不具多型能力
理解靜態型別 vs 動態型別、編譯期綁定 vs 執行期綁定,是掌握 C# 物件導向精髓的關鍵。
- 上一篇文章: C# 使用 Win32 類別庫入門(一):P/Invoke 基礎、Beep/MessageBeep、結構封送、列舉與常數
- 下一篇文章: C# 衍生類別覆寫虛擬方法完整指南:簽章一致性、override 強制、多型實作範例
Copyright ©
程式碼之家 All Rights Reserved