Effective C# 10.只對基底類別更新使用 new 修飾詞 Effective C# 10.只對基底類別更新使用 new 修飾詞 (Use the new Modifier Only to React to Base Class Updates)

Published on Thursday, October 3, 2024

這個做法在討論在衍生類別使用 new 和 override 對基底類別的影響,內容基本上與 LSP 原則重疊。

首先建立一組測試範例:

void Main()
{
	BaseClass bc = new BaseClass();
	DerivedClass dc = new DerivedClass();
	BaseClass bcdc = new DerivedClass();

	bc.Method1();
	bc.Method2();
	dc.Method1();
	dc.Method2();
	bcdc.Method1();
	bcdc.Method2();
}

class BaseClass
{
	public virtual void Method1()
	{
		Console.WriteLine("Base - Method1");
	}

	public void Method2()
	{
		Console.WriteLine("Base - Method2");
	}
}

class DerivedClass : BaseClass
{
	public override void Method1()
	{
		Console.WriteLine("Derived - Method1");
	}
	
	public new void Method2()
	{
		Console.WriteLine("Derived - Method2");
	}
}

從輸出的結果來看 bcdc 的執行結果比較沒有爭議,符合預計的結果。

Base - Method1
Base - Method2
Derived - Method1
Derived - Method2
Derived - Method1
Base - Method2

需要討論的是 bcdc 執行的結果,可以看出 dcbcdc 同樣都是 new DerivedClass(),而且同樣都是呼叫 Method1 與 Method2 唯一的區別是 bcdc 使用的是基底的類型,這個細微的差別就很容易混淆開發者,並且也違反 LSP 原則,也就是 「子型態(subtype)必須能夠替換掉它們的基底型態(base type)」。

但這也不代表要將所有基底類別的方法或屬性都設定為 virtual,這個設計叫做「預設虛擬」,此設計會帶給開發者明白自己可以修改衍生類別的方法的行為, 這種全部都預設虛擬的設計也可能讓別人認為你沒有思考衍生類型方法被修改後的結果,所以正確的做法是詳細考慮後只將需要多型的方法與屬性設定為虛擬。

另外作者建議只有一種情況才建議使用 new,也就是衍生類型實做了一個方法然後在未來基底類型也實做了相同名稱的方法時。


Summary

new 修飾詞需要小心使用,如果隨便使用它,會導致基底類型與衍生類型的方法產生不一致的行為。