Effective C# 只有當新版基類導致問題時才考慮使用new修飾符

來源:互聯網
上載者:User
我們一般在類成員上使用new修飾符,來重新定義繼承自基類的非虛成員。我們可以這麼做並不意味著我們就應該這麼做。重新定義非虛方法會導致含混不清的行為。例如,對於下面的代碼,絕大多數開發人員都會不假思索地認為它們的行為是一樣的(假設兩個類有繼承關係):

object c = MakeObject( );

// 通過MyClass引用調用:

MyClass cl = c as MyClass;

cl.MagicMethod( );

// 通過MyOtherClass引用調用:

MyOtherClass cl2 = c as MyOtherClass;

cl2.MagicMethod( );

 

如果使用了new修飾符,情況就不是這樣了:

 

public class MyClass

{

public void MagicMethod( )

{

    // 忽略細節。

}

}

public class MyOtherClass : MyClass

{

// 重新定義MagicMethod。

public new void MagicMethod( )

{

    // 忽略細節。

}

}

 

這種做法會導致許多含混不清的地方。如果在同樣的對象上調用同樣的函數,我們期望同樣的代碼被執行。但事實是,如果我們更改了用來調用函數的引用,函數調用的行為也將有所不同。這種不一致的行為看上去很荒唐。一個MyOtherClass對象,由於對它的引用不同,而有不同的行為。修飾符new並不會將一個非虛方法變為一個虛方法。相反,它會在類中添加一個不同的方法。

        非虛方法為靜態繫結。任何引用MyClass.MagicMethod()的原始碼調用的都將是該方法。系統不會在運行時尋找衍生類別中定義的其他版本。另一方面,虛函數使用的是動態綁定。系統會根據對象的運行時類型來選擇調用正確的函數。

        避免使用new修飾符來重定義非虛函數,並非意味著我們要將基類中所有的函數都定義為虛函數。當程式庫的設計者將一個函數定義為虛函數時,實際上是為類型訂立了一項合約:即表明任何衍生類別都可以更改虛函數的實現。事實上,虛函數集合定義了衍生類別中所有可能改變的行為。“預設為虛”的設計表明衍生類別可以更改類的所有行為。這意味著我們沒有仔細思考衍生類別到底會更改哪些部分的行為。我們不應該這麼做。相反,我們應該花費時間仔細考慮應該將哪些方法和屬性聲明為多態成員。我們應該僅將它們聲明為虛成員。不要認為這種做法是對類的使用者的限制。相反,應該將這種做法當作是在為定製類型行為提供一些進入點。

        僅有一種情況我們需要使用new修飾符,那就是在我們使用新版的基類後,其增添的方法名和子類中現在已經被使用的方法名衝突。因為已經有代碼在依賴子類中現有的方法名了,比如可能有其他程式集在使用這樣的方法。例如,我們通過繼承另外一個程式庫中定義的BaseWidget,定義了新的MyWidget類:

 

public class MyWidget : BaseWidget

{

public void DoWidgetThings( )

{

    // 忽略細節。

}

}

 

假設我們完成了MyWidget之後,已經有客戶在使用它。然後我們發現BaseWidget公司又發布了一個新版的BaseWidget。由於對其中的新功能抱有熱切的期待,我們立即購買了它,並試圖產生新版的MyWidget。可是,產生的時候失敗了,原因在於BaseWidget添加了自己的DoWidgetThings方法。

 

public class BaseWidget

{

public void DoWidgetThings()

{

    // 忽略細節。

}

}

 

這是一個問題,我們的基類悄無聲息地在其內引入了一個和子類同名的方法。有兩種修正該問題的方法。首先,我們可以更改DoWidgetThings方法的名字:

 

public class MyWidget : BaseWidget

{

public void DoMyWidgetThings( )

{

    // 忽略細節。

}

}


        或者,我們可以使用new修飾符:

public class MyWidget : BaseWidget

{

public new void DoWidgetThings( )

{

    // 忽略細節。

}

}

 

 

如果能訪問到MyWidget類的所有客戶程式碼,我們應該選擇更改方法的名字,因為這在長期來講比較方便。但是,如果我們的MyWidget類發行遍布全世界,那將迫使所有客戶做繁多的更改。這就是 new修飾符的用武之地了。我們的客戶可以繼續使用DoWidgetThings()方法而無需做任何更改。他們也不會調用 BaseWidget.DoWidget- Things(),因為這樣的調用在客戶代碼中不可能存在。修飾符new正是應用於這樣的場合:新版的基類增添的成員與子類中先前已經聲明的成員發生了衝突。

        當然,隨著時間的推移,我們的使用者可能也會試圖去使用BaseWidget.DoWidget- Things()方法。這時候我們又回到了原來的問題上:兩個方法看起來相同,但實際上不同。因此,我們應該考慮new修飾符所帶來的長期不良影響。有時候,短期內更改方法名所導致的不方便可能仍然是值得的。

      綜上所述,使用new修飾符必須小心。如果不分青紅皂白地使用,便會在對象上出現含混不清的方法調用。只有在“新版的基類增添的成員與子類中已存在的成員發生了衝突”這樣特殊的情況下,我們才應考慮使用 new修飾符。即使在這種情況下,在使用它之前我們也要謹慎考慮。除此之外,我們不應該再在任何其他情況下使用new修飾符。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.