Effective C# Item35:重写优于事件处理器

    .NET中许多类都提供了两种不同的途径可以处理来自系统的事件,一方面我们可以使用事件处理器,另一方面我们可以重写基类的虚方法。这两种方式有各自不同的用途,在派生类中,我们应该使用重写虚方法的方式,来处理系统事件。

    关于上述两种方式,我们看以下的代码。

代码
1 public class MyForm : Form
2 {
3
4 // Other code elided.
5
6 protected override void OnMouseDown(
7 MouseEventArgs e )
8 {
9 try {
10 HandleMouseDown( e );
11 } catch ( Exception e1 )
12 {
13 // add specific error handling here.
14 }
15 // *almost always* call base class to let
16 // other event handlers process message.
17 // Users of your class expect it.
18 base.OnMouseDown( e );
19 }
20 }
21
22
23 public class MyForm : Form
24 {
25
26 // Other code elided.
27
28 public MyForm( )
29 {
30 this.MouseDown += new
31 MouseEventHandler( this.MouseDownHandler );
32 }
33
34 private void MouseDownHandler( object sender,
35 MouseEventArgs e )
36 {
37 try {
38 HandleMouseDown( e );
39 } catch ( Exception e1 )
40 {
41 // add specific error handling here.
42 }
43 }
44 }
    上述代码使用了两种方式来实现窗口点击事件。如果在一个窗体中使用这两种方式,那么会首先执行重写的虚方法,然后才会执行事件处理器。   

    我们之所以推荐使用重写父类虚方法的方式来处理事件,是因为以下的原因:

  1. 我们知道事件是基于委托实现的,而委托和事件是都支持多播的,换句话来说,我们可以针对某一个系统事件,提供一系列方法,形成一个事件链,这个事件链中的方法会被依次调用,但是如果在调用中间某个方法的时候发生了异常,之后的方法是不会被调用的。这样的话,很可能事件在处理过程中,某个方法抛出了异常,就会导致事件链中的方法没有被全部调用。
  2. 使用重写虚方法的方式,在性能上,要比事件处理器好。这是由于窗体的事件处理机制造成的,由于窗体可能会触发的事件比较多,因此一般情况下是采用一种复杂的集合机制来存储事件处理器并把相应的处理器映射到特定的事件上,采用这种机制,在处理事件时就会占用较长的CPU时间,因为它必须查看事件是否与某个事件处理器关联上了,如果是,它就必须迭代整个请求列表,事件请求列表中的每个方法都必须被调用。查看是否有事件处理器以及在运行时迭代每一个处理器,将会比调用一个虚函数占用更多的执行时间。

    因此,我们在处理派生类中的事件时,推荐使用重写虚方法的方式来处理系统事件,这种情况包括:1)应用程序最外层控件的事件,例如窗体;2)用户自定义控件,例如我们定制一个Button时,需要重画Button的外观以及定制Button的Click事件,这时就应该采用重写虚方法的方式进行。

    在以下情况下,我们应该使用事件处理器的方式来处理系统事件:

  1. 画面中某些控件的事件,例如画面中有一个Button控件,Button有一个Click事件,这时采用重写虚方法的方式就不合适了,因为Click事件的逻辑是编写在Form上的。
  2. 由于事件是在运行时进行响应的,因此,如果我们的事件处理方法是不固定的,那么就应该采用事件处理器的方式进行,在不同的情况下,为事件绑定不同的处理方法。
posted @ 2010-01-23 20:43  李潘  阅读(434)  评论(0编辑  收藏  举报