CodeGym /課程 /C# SELF /C# 中的方法覆寫 (override)

C# 中的方法覆寫 (override)

C# SELF
等級 20 , 課堂 3
開放

1. 前言

想像一下,我們有一個用來記錄動物園動物的應用程式(就是我們之前課程開始做的那個例子啦)。我們做了一個基礎類別 Animal,裡面有個 MakeSound 方法,讓所有動物都可以「發出」某種聲音。


public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("動物發出聲音。");
    }
}
        
有 virtual 方法的基礎類別

但很快你會發現,獅子跟鸚鵡的聲音根本完全不一樣。通用的「動物發出聲音」根本不夠用。這時候就要用到 方法覆寫 啦。你需要讓每個子類別都能有自己的聲音!

2. 語法:override 怎麼用

基本規則

  • 要能覆寫方法,基礎類別裡的方法一定要標記成 virtual(或 abstract)。
  • 在子類別裡要用 override 關鍵字,方法簽名要一模一樣。

例子:獅子和鸚鵡

public class Lion : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("吼吼吼!");
    }
}

public class Parrot : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("波卡是笨蛋!");
    }
}

現在,如果你建立一個動物陣列,然後對每個動物呼叫 MakeSound,每個都會「講」自己的話:

Animal[] zoo = new Animal[]
{
    new Lion(),
    new Parrot(),
    new Animal()
};

foreach (var animal in zoo)
{
    animal.MakeSound();
}
// 輸出:
// 吼吼吼!
// 波卡是笨蛋!
// 動物發出聲音。
重點: C# 會根據物件的實際( run-time)型別來呼叫正確的方法,就算變數型別是 Animal 也一樣!

3. 虛擬方法表(v-table)

當你用 virtualoverride,編譯器會幫類別產生一個特別的「虛擬方法表」(v-table)。
當你透過基礎類別的參考呼叫方法時,CLR 會去查表:這個方法有沒有被子類覆寫?有的話就呼叫子類的版本。
這就是「晚期繫結」(late binding)或動態多型的魔法啦。

4. 組合起來:繼續進化我們的應用程式

我們再加一個類別:

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("汪汪!");
    }
}

然後在我們的小型動物園管理程式裡用它:

Animal[] zoo = new Animal[]
{
    new Lion(),
    new Parrot(),
    new Dog()
};

foreach (var animal in zoo)
{
    animal.MakeSound();
}

這樣你的程式碼擴充性跟維護性都大大提升,想加新動物超簡單超爽!

5. 覆寫屬性和帶回傳值的 override

其實不只方法可以覆寫,你也可以覆寫虛擬的 屬性 跟索引子。

public class Animal
{
    public virtual string Name { get; set; } = "動物";
}

public class Lion : Animal
{
    public override string Name { get; set; } = "獅子";
}

這在你想針對特定種類補充資訊時超方便。

6. 用 base 呼叫基礎實作

有時候你想要擴充基礎方法的行為,而不是整個取代。
這時就在覆寫的方法裡用 base 關鍵字呼叫基礎方法。

public class Parrot : Animal
{
    public override void MakeSound()
    {
        base.MakeSound(); // "動物發出聲音。"
        Console.WriteLine("波卡是笨蛋!");
    }
}

這樣鸚鵡會先發出一般「動物園」的聲音,再來自己的「波卡是笨蛋!」。

7. 實用場景:什麼時候會用到

override 的機制,幾乎是每個認真寫 C# 的 OOP 專案都必備的技能。

  • 我們動物園的動物模型? 已經做過啦。
  • 自訂 UI 控制項: 你會覆寫標準的視覺方法,加入自己的邏輯。
  • 彈性的商業邏輯: 可以在基礎類別定「骨架」,細節讓子類自己搞定。
  • 測試跟 mock: 很容易用繼承來「取代」方法做單元測試。
  • 外掛和擴充: 介面或抽象基礎類別,多種實作都靠正確覆寫來運作。

8. sealed override 跟虛擬方法鏈

如果你不想讓別人再往下覆寫你已經覆寫過的方法,可以用 sealed override

public class Base
{
    public virtual void Foo() { }
}

public class Middle : Base
{
    public sealed override void Foo() { }
}

public class Last : Middle
{
    public override void Foo() {} // 錯誤 — 不能再覆寫!
}

這樣可以在你覺得很重要的地方「封住」覆寫鏈,確保系統正確運作。

9. 新方法(new 關鍵字)

有時你想在子類別裡宣告一個同名方法,但基礎類別的不是 virtual
這時可以用 new 關鍵字——但這不是多型,只是「遮蔽」而已:

public class Animal
{
    public void MakeSound()
    {
        Console.WriteLine("我是動物!");
    }
}

public class Cat : Animal
{
    public new void MakeSound()
    {
        Console.WriteLine("我是貓咪!");
    }
}

Animal animal = new Cat();
animal.MakeSound(); // "我是動物!"
Cat cat = new Cat();
cat.MakeSound(); // "我是貓咪!"

這裡呼叫時完全看變數型別!所以要真正的動態多型,還是要用 virtual + override 組合。

10. 覆寫方法時常見錯誤

錯誤一: 想覆寫一個沒標 virtualabstractoverride 的方法。
在 C# 裡,普通方法不能被覆寫。如果基礎類別的方法沒特別標(virtualabstractoverride),你在子類別寫 override 會直接編譯錯誤。

錯誤二: 方法簽名不一樣。
要能真的覆寫,方法名稱、回傳型別、參數都要跟基礎類別一模一樣。只要有一點不同(像是參數型別不同),就會變成新方法,不是覆寫。

錯誤三: 忘記加 override 修飾詞。
如果你寫了一個跟基礎類別一樣簽名的方法,但沒加 override,編譯器不會當成覆寫。這叫做 方法遮蔽(之後會講)。這樣你用基礎型別變數呼叫時,會呼叫到基礎類別的方法,不是你寫的那個。

2
任務
C# SELF, 等級 20, 課堂 3
上鎖
多型與方法覆寫
多型與方法覆寫
2
任務
C# SELF, 等級 20, 課堂 3
上鎖
透過 `base` 使用基礎實作
透過 `base` 使用基礎實作
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION