共用方式為


使用 Override 和 New 關鍵字進行版本控制 (C# 程式設計手冊)

更新:2007 年 11 月

C# 語言對於控制不同程式庫的 base 類別和衍生類別版本,是設計成可以不斷改進,同時也能維持回溯相容性。舉例來說,這表示 C# 完全支援在基底 class 中加入新成員時,其名稱可與衍生類別中的成員相同,而且不會導致未預期的行為。它同時表示,類別必須明確敘述方法是否要覆寫繼承方法,或者方法是否為隱藏與繼承方法相似名稱的一個新方法。

在 C# 中,衍生類別可以包含擁有與基底類別方法相同之名稱的方法。

  • 基底類別方法必須定義為 virtual

  • 如果衍生類別中的方法前面沒有加上 newoverride 關鍵字,編譯器會發出警告,且方法的行為會如同有 new 關鍵字一般。

  • 如果衍生類別中的方法前面加上 new 關鍵字,該方法會定義為與基底類別中的方法無關。

  • 如果衍生類別中的方法前面加上 override 關鍵字,則衍生類別的物件會呼叫該方法,而不是基底類別方法。

  • 衍生類別可使用 base 關鍵字呼叫基底類別方法。

  • override、virtual 和 new 關鍵字也可用於屬性、索引子和事件。

根據預設,C# 方法是非虛擬的。如果將方法宣告為虛擬,則任何繼承該方法的類別都可以實作自己的版本。若要使方法成為虛擬,可以在基底類別的方法宣告中使用 virtual 修飾詞。衍生類別接著便能夠使用 override 關鍵字來覆寫基底虛擬方法,或是使用 new 關鍵字來隱藏基底類別中的虛擬方法。如果未指定 override 或 new 關鍵字,編譯器會發出警告,而且衍生類別中的方法將會隱藏基底類別中的方法。如需詳細資訊,請參閱編譯器警告 (層級 2) CS0108

以實際情況為例,可以假設 A 公司建立了名為 GraphicsClass 的類別以供您的程式使用。下列是 GraphicsClass:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
}

您的公司會使用這個類別,您則用來衍生自己的類別並加入新方法:

class YourDerivedGraphicsClass : GraphicsClass
{
    public void DrawRectangle() { }
}

您的應用程式使用起來很正常,直到 A 公司發行新版的 GraphicsClass,類似下列程式碼:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
    public virtual void DrawRectangle() { }
}

新版的 GraphicsClass 現在包含名為 DrawRectangle 的方法。一開始並不會發生任何問題。新版本仍然可以與舊版本二進位相容。您所部署的任何軟體都會繼續執行,就算電腦系統上安裝了新的類別也不會有問題。目前對方法 DrawRectangle 所做的任何呼叫,仍會參考您自己衍生類別中的方法版本。

不過,一旦您使用新版 GraphicsClass 重新編譯應用程式,便會收到編譯器發出的警告。如需詳細資訊,請參閱編譯器警告 (層級 2) CS0108

這個警告會通知您,讓您考量自己的 DrawRectangle 方法應如何在應用程式中執行。

如果您要讓自己的方法覆寫新的基底類別方法,請使用 override 關鍵字:

class YourDerivedGraphicsClass : GraphicsClass
{
    public override void DrawRectangle() { }
}

override 關鍵字可確保從 YourDerivedGraphicsClass 衍生的任何物件都會使用衍生類別版本的 DrawRectangle。不過,從 YourDerivedGraphicsClass 衍生的物件仍可使用 base 關鍵字存取基底類別版本的 DrawRectangle:

base.DrawRectangle();

如果您不要讓自己的方法覆寫新的基底類別方法,則適用下列考量:為避免兩個方法之間的混淆,您可以重新命名方法。這可能相當耗時間而且容易發生錯誤,在某些情況下並不是實際的做法。不過,如果您的專案不大,可以使用 Visual Studio 的「重構」選項重新命名方法。如需詳細資訊,請參閱重構類別和型別

此外,您也可以在衍生類別定義中使用關鍵字 new,以避免出現警告:

class YourDerivedGraphicsClass : GraphicsClass
{
    public new void DrawRectangle() { }
}

使用 new 關鍵字即告知編譯器,您的定義將隱藏基底類別中所包含的定義。這是預設行為。

覆寫和方法選擇

如果方法是在類別上指定,而在呼叫時有兩個方法相容時,C# 編譯器會選擇呼叫最適合的方法,比方有兩個同名的方法,而且參數與傳遞的參數相容時。例如,下列方法可能會相容:

public class Derived : Base
{
    public override void DoWork(int param) { }
    public void DoWork(double param) { }
}

當在 Derived 的執行個體上呼叫 DoWork 時,C# 編譯器會先嘗試呼叫原先在 Derived 上宣告的 DoWork 版本。覆寫方法並不是在類別上宣告的,而是基底類別上所宣告方法的新實作。只有當 C# 編譯器無法比對方法呼叫與 Derived 上的原始方法時,才會嘗試比對呼叫與同名而且參數相容的覆寫方法。例如:

int val = 5;
Derived d = new Derived();
d.DoWork(val);  // Calls DoWork(double).

由於變數 val 可隱含轉換成雙精度浮點數,因此 C# 編譯器會呼叫 DoWork(double) 而非 DoWork(int)。避免這情況的方法有兩個。第一是避免將新方法宣告為與虛擬方法同名。第二則是指示 C# 編譯器將 Derived 的執行個體轉換成 Base,然後藉由搜尋基底類別方法清單的方式呼叫虛擬方法。由於方法為虛擬,因此會呼叫 Derived 上 DoWork(int) 的實作。例如:

((Base)d).DoWork(val);  // Calls DoWork(int) on Derived.

請參閱

概念

C# 程式設計手冊

參考

類別和結構 (C# 程式設計手冊)

方法 (C# 程式設計手冊)

繼承 (C# 程式設計手冊)