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

衣舞晨風發表於2015-06-27

原文地址:點選開啟連結

C# 語言經過專門設計,以便不同庫中的基類與派生類之間的版本控制可以不斷向前發展,同時保持向後相容。 這具有多方面的意義。例如,這意味著在基中引入與派生類中的某個成員具有相同名稱的新成員在 C# 中是完全支援的,不會導致意外行為。 它還意味著類必須顯式宣告某方法是要重寫一個繼承方法,還是一個隱藏具有類似名稱的繼承方法的新方法。

在 C# 中,派生類可以包含與基類方法同名的方法。

  • 基類方法必須定義為 virtual

  • 如果派生類中的方法前面沒有 new 或 override 關鍵字,則編譯器將發出警告,該方法將有如存在 new 關鍵字一樣執行操作。

  • 如果派生類中的方法前面帶有 new 關鍵字,則該方法被定義為獨立於基類中的方法。

  • 如果派生類中的方法前面帶有 override 關鍵字,則派生類的物件將呼叫該方法,而不是呼叫基類方法。

  • 可以從派生類中使用 base 關鍵字呼叫基類方法。

  • override virtual 和 new 關鍵字還可以用於屬性、索引器和事件中。

預設情況下,C# 方法為非虛方法。 如果某個方法被宣告為虛方法,則繼承該方法的任何類都可以實現它自己的版本。 若要使方法成為虛方法,必須在基類的方法宣告中使用 virtual 修飾符。 然後,派生類可以使用 override 關鍵字重寫基虛方法,或使用 new 關鍵字隱藏基類中的虛方法。 如果 override 關鍵字和 new 關鍵字均未指定,編譯器將發出警告,並且派生類中的方法將隱藏基類中的方法。

為了在實踐中演示上述情況,我們暫時假定公司 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 的新版本重新編譯應用程式,就會收到來自編譯器的警告 CS0108。 此警告提示您必須考慮希望 DrawRectangle 方法在應用程式中的工作方式。

如果您希望自己的方法重寫新的基類方法,請使用 override 關鍵字:

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

override 關鍵字可確保派生自 YourDerivedGraphicsClass 的任何物件都將使用 DrawRectangle 的派生類版本。 派生自 YourDerivedGraphicsClass 的物件仍可以使用基關鍵字訪問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 可以隱式轉換為 double 型別,因此 C# 編譯器將呼叫 DoWork(double),而不是 DoWork(int)。 有兩種方法可以避免此情況。 首先,避免將新方法宣告為與虛方法同名。 其次,可以通過將 Derived 的例項強制轉換為 Base 來使 C# 編譯器搜尋基類方法列表,從而使其呼叫虛方法。 由於是虛方法,因此將呼叫 Derived 上的 DoWork(int) 的實現。 例如:

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

有關 new 和 override的更多示例,請參見 瞭解何時使用 Override 和 New 關鍵字(C# 程式設計指南)


相關文章