多態是物件導向程式設計的核心特性之一。當我們希望基類定義一個通用介面,而衍生類提供具體實現時,通常使用 virtual 和 override 關鍵字。
class Base
{
public virtual void PrintStatus()
{
Console.WriteLine("public virtual void PrintStatus() in Base");
}
}
public void DisplayStatusOf(Base[] bs)
{
foreach (Base b in bs)
{
b.PrintStatus(); // 多態調用
}
}
這種設計允許我們忽略具體類型差異,以統一方式處理不同物件,正如 .NET 中 XmlReader.Create(Stream) 可接受 FileStream、MemoryStream、NetworkStream 等任何串流類型。
若衍生類未使用 override 而是使用 new 關鍵字定義同名方法,會發生什麼?
class Derived1 : Base
{
public override void PrintStatus()
{
Console.WriteLine("public override void PrintStatus() in Derived1");
}
}
class Derived2 : Base
{
public new void PrintStatus()
{
Console.WriteLine("public new void PrintStatus() in Derived2");
}
}
Base[] bs = new Base[] { new Base(), new Derived1(), new Derived2() };
DisplayStatusOf(bs);
// 輸出:
// public virtual void PrintStatus() in Base
// public override void PrintStatus() in Derived1
// public virtual void PrintStatus() in Base ← Derived2 未生效!
Derived2 使用 new 只是「隱藏」了基類方法,而非覆寫。透過基類參考調用時,仍會執行基類實現。
new 關鍵字並未刪除基類方法,只是在衍生類中遮蔽了它。我們仍可透過類型轉換「解封」訪問:
Base d2 = new Derived2();
d2.PrintStatus(); // 輸出基類方法
class Derived2 : Base
{
public new void PrintStatus()
{
base.PrintStatus(); // 透過 base 關鍵字調用基類版本
Console.WriteLine("public new void PrintStatus() in Derived2");
}
}
對於靜態成員,解封需透過類別名稱:Base.StaticMethod()。
雖然 new 容易破壞多態契約,但恰當使用可彌補基類設計不足。例如 Rockford Lhotka 在《Creating a Data Bound ListView Control》中示範如何用 new 封裝 ListView.Columns 並替換為自定義集合。
virtual + override:實現真正的運行時多態new:編譯時綁定,隱藏基類成員,不影響基類實現override