C# 語言經過專門設計,以便不同庫中的基類與派生類之間的版本控制可以不斷向前發展,同時保持後向相容。 這具有多方面的意義。例如,這意味著在基類中引入與派生類中的某個成員具有相同名稱的新成員在 C# 中是完全支持的,不會導致意外行為。 它還意味著類必須顯式聲明某方法是要替代一個繼承方法,還是本身就是一 ...
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() { }
}
公司使用此類,並且你在添加新方法時將其用於派生自己的類:
C#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
的對象仍可以使用 base 關鍵字訪問 DrawRectangle
的基類版本:
base.DrawRectangle();
如果不需要自己的方法替代新的基類方法,則需要註意以下事項。 為了避免這兩個方法之間發生混淆,可以重命名你的方法。 這可能很耗費時間且容易出錯,而且在某些情況下並不可行。 但是,如果項目相對較小,則可以使用 Visual Studio 的重構選項來重命名方法。 有關詳細信息,請參閱重構類和類型(類設計器)。
或者,也可以通過在派生類定義中使用關鍵字 new
來防止出現該警告:
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
上的原始方法匹配時,才嘗試將該調用與具有相同名稱和相容參數的替代方法匹配。 例如:
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.