Effective C# Item 35: Prefer Overrides to Event Handlers
許多.net類庫中的類都提供了兩種不同的處理事件控制代碼的方法。既可以為其添加事件,也可以重寫其基類的事件抽象方法。為什麼要為同一件事提供兩種不同的方法呢?這是為了對應不同的情況。在實現衍生類別的時候,更好的選擇是重寫基類中的抽象方法。
假設我們現在正在編寫一個windows應用程式,這個程式需要對滑鼠按鍵按下的事件做出響應。在自訂的Form類中,我們可以選擇重寫OnMouseDown()事件:
public class MyForm : Form
{
protected override void OnMouseDown(MouseEventArgs e)
{
try
{
HandleMouseDown(e);
}
catch
{
//error
}
base.OnMouseDown(e);
}
}
或者添加事件控制代碼:
public class MyForm : Form
{
public MyForm()
{
this.MouseDown += new MouseEventHandler(MyForm_MouseDown);
}
void MyForm_MouseDown(object sender, MouseEventArgs e)
{
try
{
HandleMouseDown(e);
}
catch
{
//error
}
}
}
這裡推薦使用第一種方法。一旦事件控制代碼拋出異常,不會再有其他的事件控制代碼被調用。這避免了一些錯誤碼繼續被調用而引發的問題。通過重寫受保護的虛方法,我們的控制代碼可以第一個被調用。基類中虛函數負責其他相關控制代碼的調用。這意味著如果需要調用那些事件控制代碼(一般來說是需要的),就要調用基類的虛函數。在有些特殊情況下我們需要替換基類的預設行為,可能不需要調用任何原有的事件控制代碼。雖然我們不能保證所有的事件控制代碼都被執行,因為其可能會拋出異常,但是我們可以保證衍生類別的行為是正確的。
使用override比添加事件控制代碼高效的多。在Item 22中展示了System.Windows.Forms.Control類是如何儲存控制代碼時間並將其對應到每一個事件的。這種事件機制由於要檢查事件控制代碼將造成更多的消耗。事件控制代碼列表中的每個方法都需要執行。相比重寫虛方法,通過事件處理會消耗更多的時間。
如果這還不足以說服你,咱們可以再回頭看一下開始時候的代碼。哪一種更清晰?重寫虛方法只需要維護一個函數就可以達到檢查和修改的目的。而事件機制需要兩個維護點:事件控制代碼函數和事件綁定代碼。其中任何一點都可能造成整體功能上的失敗。一個函數顯然要簡單些。
這些就是使用重寫和不是事件控制代碼的理由。但是.net設計者之所以會提供事件也是有其理由的。他們不會做無用的工作。這種重寫是針對衍生類別的。除此之外的情況我們必須使用事件機制。例如我們經常需要為Form添加一個按鈕的點擊事件。這個事件由按鈕引發,在form中處理。當然我們可以搞一個自訂按鈕,然後在裡面重寫點擊的虛方法,但這太繁瑣了,為了處理一個事件,需要建立一個自訂的按鈕類,完全是在給自己找麻煩。而使用事件機制就非常的簡單。這也是.net framework設計者在設計事件機制的一個理由。
另外一個理由是事件的綁定是在運行期進行的。我們可以更加靈活的處理事件,在運行時為其綁定不同的事件。假設我們正在編寫一個繪圖程式,點擊滑鼠可能是畫線的開始,也可能是選取某個對象。我們可以在使用者切換模式的時候來切換這些事件。而且,我們可以為同一個事件添加多個事件控制代碼。
當我們建立衍生類別時,應當使用重寫虛函數的方法來處理事件。這樣便於維護也更加高效。其他情況則應該使用事件控制代碼。
譯自 Effective C#:50 Specific Ways to Improve Your C# Bill Wagner著
回到目錄