程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Effective C#原則35:選擇重寫函數而不是使用事件句柄

Effective C#原則35:選擇重寫函數而不是使用事件句柄

編輯:關於C#

很多.Net類提供了兩種不同的方法來控制一些系統的事件。那就是,要麼添 加一個事件句柄;要麼重寫基類的虛函數。為什麼要提供兩個方法來完成同樣的 事情呢?其實很簡單,那就是因為不同的情況下要調用為的方法。在派生類的內 部,你應該總是重寫虛函數。而對於你的用戶,則應該限制他們只使用句柄來響 應一些不相關的對象上的事件。

例如你很了一個很不錯的Windows應用程 序,它要響應鼠標點下的事件。在你的窗體類中,你可以選擇重寫OnMouseDown ()方法:

public class MyForm : Form
{
 // Other code elided.
 protected override void OnMouseDown(
   MouseEventArgs e )
 {
  try {
   HandleMouseDown( e );
  } catch ( Exception e1 )
  {
   // add specific error handling here.
  }
  // *almost always* call base class to let
  // other event handlers process message.
  // Users of your class expect it.
   base.OnMouseDown( e );
 }
}

或者你可以添加一個 事件句柄:

public class MyForm : Form
{
 // Other code elided.
 public MyForm( )
 {
   this.MouseDown += new
   MouseEventHandler( this.MouseDownHandler );
 }
 private void MouseDownHandler( object sender,
  MouseEventArgs e )
  {
  try {
   HandleMouseDown( e );
  } catch ( Exception e1 )
  {
   // add specific error handling here.
  }
 }
}

前面一些方法要好一些,如 果在事件鏈上有一個句柄拋出了一個異常,那麼其它的句柄都不會再被調用(參 見原則21)。一些“病態”的代碼會阻止系統調用事件上的句柄。通 過重寫受保護的虛函數,你的控制句柄會就先執行。基類上的虛函數有責任調用 詳細事件上的所有添加的句柄。這就是說,如果你希望事件上的句柄被調用(而 且這是你最想完成的),你就必須調用基類。而在一些罕見的類中,你希望取代 基類中的默認事件行為,這樣可以讓事件上的句柄都不被執行。你不去保證所所 的事件句柄都將被調用,那是因為一些“病態”事件句柄可能會引發 一些異常,但你可以保證你派生類的行為是正確的。

使用重載比添加事 件句柄更高效。我已經在原則22中告訴過你,System.Windows.Forms.Control類 是如何世故的使用任命機制來存儲事件句柄,然後映射恰當的句柄到詳細的事件 上。這種事件機制要花上更多的處理器時間,那是因為它必須檢測事件,看它是 否有事件句柄添加在上面。如果有,它就必須迭代整個調用鏈表。方法鏈表中的 每個方法都必須調用。斷定有哪些事件句柄在那裡,還要對它們進行運行時迭代 ,這與只調用一個虛函數來說,要花上更多的執行時間。

如果這還不足 以讓你決定使用重載,那就再看看這一原則一開始的鏈表。那一個更清楚?如果 重載虛函數,當你在維護這個窗體時,只有一個函數要檢查和修改。而事件機制 則有兩個地方要維護:一個就是事件句柄,另一就是事件句柄上的函數。任何一 個都可能出現失敗。就一個函數更簡單一些。

OK,我已經給出了所有要 求使用重載而不是事件句柄的原因。.Net框架的設計者必須要添加事件給某人, 對嗎?當然是這樣的。就你我們剩下的內容一個,他們太忙了而沒時間寫一些沒 人使用的代碼。重寫只是為派生類提供的,其它類必須使用事件機制。例如,你 經常添加一個按鈕點擊事件到一個窗體上。事件是由按鈕觸發的,但是由窗體對 象處理著事件。你完全可以在這個類中定義一個用戶的按鈕,而且重寫這個點擊 句柄,但這對於只是處理一個事件來說花上了太多的代碼。不管怎樣,問題都是 交給你自己的類了:你自己定義的按鈕還是在點擊時必須與窗體進行通信。顯然 應該用事件來處理。因此,最後,你只不過是創建了一個新類來向窗體發送事件 (譯注:其實我們完全可以創建這個類不用發事件給窗體就可以完成回調的,只 是作者習慣的說什麼好就一味的否定其它。但不管怎樣,重寫一個按鈕來重載函 數確實不是很值。)。 相對前面一種方法,直接在窗體事件添加句柄要簡單得多 。這也就是為什麼.Net框架的設計者把事件放在窗體的最前面。

另一個 要使用事件的原因就是,事件是在運行時處理的。使用事件有更大的伸縮性。你 可以在一個事件上添加多個句柄,這取決於程序的實際環境。假設你寫了一個繪 圖程序,根據程序的狀態,鼠標點下時應該畫一條線,或者這它是要選擇一個對 象。當用戶切換功能模式時,你可以切換事件句柄。不同的類,有著不同的事件 句柄,而處理的事件則取決於應用程序的狀態。

最後,對於事件,你可 以把多個事件句柄掛到同樣的事件上。還是想象同樣的繪圖程序,你可能在 MouseDown事件上掛接了多個事件句柄。第一個可能是完成詳細的功能,第二個 可能是更新狀態條或者更新一些可訪問的不同命令。不同的行為可以在同一事件 上響應。

當你有一個派生類中只有一個函數處理一個事件時,重載是最 好的方法。這更容易維護,今後也會更正確,而且更高效。而應該為其它用戶保 留事件。因此,我們應該選擇重寫基類的實現而不是添加事件句柄。

返回教程目錄

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