可用 new 修飾符重新定義從基類繼承的非虛擬成員。能這樣做不代表需要這樣做。重新定義非虛擬方法導致方法含義混亂。若兩相關類別是繼承關係,許多開發人員立即假設兩程式碼塊做完全相同事,也會這麼認為:
object c = MakeObject();
MyClass cl = c as MyClass; cl.MagicMethod();
MyOtherClass cl2 = c as MyOtherClass; cl2.MagicMethod();
一旦用 new 修飾符,問題完全不同:
public class MyClass { public void MagicMethod() { /* details */ } }
public class MyOtherClass : MyClass
{
public new void MagicMethod() { /* details */ }
}
這實際操作讓許多開發人員困惑。因同一物件上調用相同函數,一定希望執行相同程式碼。但實際上,用不同參考調用同名函數,行為不一樣,感覺非常糟糕。它們不一致。MyOtherClass 型別物件表現行為因參考方式不同而不同。這是 new 修飾符用在非虛擬成員的後果。其實只是在類別名稱空間添加不同方法(雖函數名相同)。
非虛擬方法靜態繫結,不管哪裡程式碼、哪裡參考,MyClass.MagicMethod() 總是嚴格調用類別中定義函數。不會在執行期在衍生類別查找不同版本。虛擬函數動態:執行期根據不同型別物件調用不同版本。
建議避免用 new 修飾符重新定義非虛擬函數,不需太多解釋,如同建議定義基類時用虛擬方法。類別庫設計者應按合約設計虛擬函數。表示期望任何衍生類別修改虛擬函數實現。虛擬函數集合相當於定義行為集合,這些行為希望在衍生中重新實現。設計預設虛擬函數即說衍生可修改類別中所有虛擬行為。這確實是說你不想考慮所有衍生類別可能修改行為的分歧問題。相反,把時間花在考慮把什麼方法、屬性設計成多態。只有虛擬行為時才能這樣做。別認為這限制類別使用者。相反,應認為這為型別使用者定義行為提供入口向導。
有且只有一種情況要用 new 修飾符:將類別繼承到已存在基類上時,而基類中已使用存在方法名,這時就要用 new(譯註:基類與衍生類都已存在,後來添加繼承關係,發現兩類別用同樣方法名,可在衍生類別添加 new 解決)。因有些程式碼已依賴類別方法名,或其他組件在使用此方法。例如你在庫中建立類別,使用另一庫中定義的 BaseWidget:
public class MyWidget : BaseWidget { public void DoWidgetThings() { /* details */ } }