程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Effective C#原則29:僅在對基類進行強制更新時才使用new修飾符

Effective C#原則29:僅在對基類進行強制更新時才使用new修飾符

編輯:關於C#

你可以用new修飾符來重新定義一個從基類中繼承來的非虛成員。你可以這樣 做,但並不意味著需要這樣做。重新定義非虛方法會導致方法含意的混亂。如果 兩個相關的類是繼承關系,那麼很多開發人員可能會立即假設兩段代碼塊是做完 全相同的事情,而且他們也會這麼認為:

object c = MakeObject( );
// Call through MyClass reference:
MyClass cl = c as MyClass;
cl.MagicMethod( );
// Call through MyOtherClass reference:
MyOtherClass cl2 = c as MyOtherClass;
cl2.MagicMethod( );

一但使用了new修飾符以後,問題就完全 不一樣了:

public class MyClass
{
 public void MagicMethod( )
 {
  // details elided.
 }
}
public class MyOtherClass : MyClass
{
 // Redefine MagicMethod for this class.
 public new void MagicMethod( )
 {
  // details elided
 }
}

這樣的實 際操作會讓很多開發人員迷惑。因為當你在同一個對象上調用相同的函數時,一 定希望它們執行同樣的代碼。但實際上是,一但你用不同的引用來調用同名的函 數,它們的行為是不一樣的,這感覺非常糟糕。它們是不一致的。一個 MyOtherClass類型的對象所表現的行為會因為你引用的方式不一樣而有所不同。 這就是new修飾符用在非虛成員上的後果。其實這只是讓你在類的名字空間中添 加了一個不同的方法(雖然它們的函數名是相同的)。

非虛方法是靜態綁 定的,不管哪裡的代碼,也不管在哪裡引用,MyClass.MagicMethod() 總是嚴格 的調用類中所定義的函數。並不會在運行時在派生類中查找不同的版本。另一方 面,虛函數動態的。運行時會根據不同的類型對象調用不同的版本。

建 議大家避免使用new修飾符來重新定義非虛函數,這並不要太多的解釋,就像推 薦大家在定義一個基類時應該用虛方法一樣。一個類庫的設計者應該按照合某種 約設計虛函數。也就表示你期望任何派生類都應該修改虛函數的實現。虛函數的 集合就相當於是定義了一個行為的集合,這些行為是希望在派生中重新實現的。 設計默認的虛函數就是說派生可以修改類中的所有虛的行為。這確實是說你不想 考慮所有派生類可能要修改行為的分歧問題。相反,你可以把時間花在考慮把什 麼樣的方法以及屬性設計成多態的。當然,只有它們是虛行為的時候才能這樣做 。不要考慮這樣會限制類的用戶。相反,應該認為這是給類型的用戶定義行為提 供了一個入口向導。

有且只有一種情況要使用new修飾符,那就是把類集 成到一個已經存在的基類上時,而這個基類中已經使用了存在的方法名,這時就 要使用new了(譯注:就是說基類與派生類都已經存在了,是後來添加的繼承關系 ,結果在添加繼承關系時,發現兩個類中使用了同樣的方法名,那麼就可以在派 生類中添加一個new來解決這個問題)。因為有些代碼已經依懶於類的方法名,或 者已經有其它程序集在使用這個方法。例如你在庫中創建了下面的類,使用了在 另一個庫中定義的BaseWidget:

public class MyWidget : BaseWidget
{
 public void DoWidgetThings( )
 {
  // details elided.
 }
}

你完成了你的 widget, 而且用戶可以使用它。然而你卻發現BaseWidget公司發布了一個新的版 本。而這正是你所渴望的,於是你立即購買並編譯你的MyWidget類。結果失敗了 ,因為BaseWidget的家伙們已經添加了他們自己的DoWidgetThings 方法:

public class BaseWidget
{
 public void DoWidgetThings()
 {
  // details elided.
 }
}

這是個難題,你的基類中隱藏了一個方法,而這又是在你的類的 名字空間中。有兩個方法解決這個問題,一個就是修改你的類中的方法名:

public class MyWidget : BaseWidget
{
 public void DoMyWidgetThings( )
 {
  // details elided.
 }
}

或者使用new修飾符:

public class MyWidget : BaseWidget
{
 public new void DoWidgetThings( )
 {
  // details elided.
 }
}

如 果你可以拿到所有使用MyWidget類的源代碼,那麼你應該選擇修改方法名,因為 這對於今後的運行會更簡單。然而,如果你已經向全世界的人發布了MyWidget類 ,這會迫使所有用戶來完成這個眾多的改變。這正是new修飾符容易解決的問題 ,你的用戶不用修改DoWidgetThings()方法而繼續使用它。沒有人會調用到 BaseWidget.DoWidgetThings()方法,因為(對於派生類而言)它們根本不存在。 在更新一個基類時,如果發現它與先前申明的成員發生了沖突,可以用new修飾 符來解決這個問題。

當然,在某些時候,你的用戶可能想調用基類的 Widget.DoWidgetThings()方法,這時你又回到了原來的問題上:兩個方法看上 去是一樣的,但其實是不同的。考慮到new修飾長期存在的歧意問題,有時候, 還是在短期上麻煩一下,修改方法名為上策。(譯注:長痛不如短痛。呵呵)

new修飾符必須小心謹慎的使用。如果它是有歧意的,你就在類上創建了 個模糊的方法。這只有在特殊情況下才使用,那就是升級基類時與你的類產生沖 突時。即使在這種情況下,也應該小心的使用它。最重要的是,其它任何時候都 不要用它。

返回教程目錄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved