很多.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事件上掛接了多個事件句柄。第一個可能是完成詳細的功能,第二個 可能是更新狀態條或者更新一些可訪問的不同命令。不同的行為可以在同一事件 上響應。
當你有一個派生類中只有一個函數處理一個事件時,重載是最 好的方法。這更容易維護,今後也會更正確,而且更高效。而應該為其它用戶保 留事件。因此,我們應該選擇重寫基類的實現而不是添加事件句柄。
返回教程目錄