C# 語言經過專門設計,以便不同庫中的基類與衍生類別之間的版本控制可以不斷向前發展,同時保持向後相容。這具有多方面的意義。例如,這意味著在基類中引入與衍生類別中的某個成員具有相同名稱的新成員在 C# 中是完全支援的,不會導致意外行為。它還意味著類必須顯式聲明某方法是要重寫一個繼承方法,還是一個僅隱藏具有類似名稱的繼承方法的新方法。
C# 允許衍生類別包含與基類方法名稱相同的方法。
基類方法必須定義為 virtual。
如果衍生類別中的方法前面沒有 new 或 override 關鍵字,則編譯器將發出警告,該方法將有如存在 new 關鍵字一樣執行操作。
如果衍生類別中的方法前面帶有 new 關鍵字,則該方法被定義為獨立於基類中的方法。
如果衍生類別中的方法前面帶有 override 關鍵字,則衍生類別的對象將調用該方法,而不調用基類方法。
可以從衍生類別中使用 base 關鍵字調用基類方法。
override、virtual 和 new 關鍵字還可以用於屬性、索引器和事件中。
預設情況下,C# 方法不是虛方法 -- 如果將一種方法聲明為虛方法,則繼承該方法的任何類都可以實現其自己的版本。若要使方法成為虛方法,必須在基類的方法聲明中使用 virtual 修飾符。然後,衍生類別可以使用 override 關鍵字重寫基虛方法,或使用 new 關鍵字隱藏基類中的虛方法。如果 override 關鍵字和 new 關鍵字均未指定,編譯器將發出警告,並且衍生類別中的方法將隱藏基類中的方法。有關更多資訊,請參見編譯器警告 CS0108。
為了在實踐中示範上述情況,我們暫時假定公司 A 建立了一個名為 GraphicsClass 的類,您的程式使用該類。GraphicsClass 類似如下:
C# 複製代碼
class GraphicsClass{public virtual void DrawLine() { }public virtual void DrawPoint() { }}
您的公司使用此類,並且您在添加新方法時將其用來派生自己的類:
C# 複製代碼
class YourDerivedGraphicsClass : GraphicsClass{public void DrawRectangle() { }}
您在應用程式的使用過程中沒有遇到任何問題,直到公司 A 發布了 GraphicsClass 的新版本,該新版本類似如下:
C# 複製代碼
class GraphicsClass{public virtual void DrawLine() { }public virtual void DrawPoint() { }public virtual void DrawRectangle() { }}
現在,GraphicsClass 的新版本中包含了一個稱為 DrawRectangle 的方法。最初,一切正常。新版本仍與舊版本二進位相容 -- 即使在電腦系統中安裝新類,部署的所有軟體仍將繼續工作。在您的衍生類別中,對方法 DrawRectangle 的任何現有調用將繼續引用您的版本。
但是,一旦使用 GraphicsClass 的新版本重新編譯應用程式,您將收到來自編譯器的警告。有關更多資訊,請參見編譯器警告 CS0108。
此警告提示您需要考慮您的 DrawRectangle 方法在應用程式中的工作方式。
如果想用您的方法重寫新的基類方法,請使用 override 關鍵字,如下所示:
C# 複製代碼
class YourDerivedGraphicsClass : GraphicsClass{public override void DrawRectangle() { }}
override 關鍵字可確保派生自 YourDerivedGraphicsClass 的任何對象都將使用 DrawRectangle 的衍生類別版本。派生自 YourDerivedGraphicsClass 的對象仍可以使用 base 關鍵字訪問 DrawRectangle 的基類版本,如下所示:
C# 複製代碼
base.DrawRectangle();
如果不想用您的方法重寫新的基類方法,則應注意下面的事項。為避免在兩種方法之間引起混淆,可以重新命名您的方法。重新命名方法可能很耗時且容易出錯,而且在某些情況下並不實用。但是,如果您的項目相對較小,則可以使用 Visual Studio 的重構選項來重新命名方法。有關更多資訊,請參見重構類和類型。
或者,也可以通過在衍生類別定義中使用關鍵字 new 來防止出現該警告,如下所示:
C# 複製代碼
class YourDerivedGraphicsClass : GraphicsClass{public new void DrawRectangle() { }}
使用 new 關鍵字告訴編譯器您的定義將隱藏基類中包含的定義。這是預設行為。
重寫和方法選擇
當在類中指定方法時,如果有多個方法與調用相容(例如,存在兩種同名的方法,並且其參數與傳遞的參數相容),則 C# 編譯器將選擇最佳方法進行調用。下面的方法將是相容的:
C# 複製代碼
public class Derived : Base{public override void DoWork(int param) { }public void DoWork(double param) { }}
在 Derived 的一個執行個體中調用 DoWork 時,C# 編譯器將首先嘗試使該調用與最初在 Derived 上聲明的 DoWork 版本相容。重寫方法不被視為是在類上進行聲明的,而是在基類上聲明的方法的新實現。僅當 C# 編譯器無法將方法調用與 Derived 上的原始方法匹配時,它才嘗試將該調用與具有相同名稱和相容參數的重寫方法匹配。例如:
C# 複製代碼
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) 的實現。例如:
C# 複製代碼
((Base)d).DoWork(val); // Calls DoWork(int) on Derived.