正确使用Event(VB.NET)

来源:互联网 发布:存款收益率怎么算法 编辑:程序博客网 时间:2024/04/30 20:49

原文出处 作者:Ninputer

一、如何定义和引发事件

定义事件有两种语法,一种是显式指定事件处理方法的委托类型:

[modifier] Event EventName As HandlerType

另一种是直接在定义语句中写出事件处理方法的参数,这是一种隐式指定委托类型的方法:

[modifier] Event EventName(args)

在这种定义下VB会根据参数表生成一个嵌套定义的委托类型。我建议大多数情况使用第一种定义方法,因为第一种方法可以将事件处理方法的委托类型独立出来,防止无意中创建大量浪费的类型。比如下面两个事件:

Public Event Test(ByVal sender As Object, ByVal e As EventArgs)
Public Event Test1(ByVal sender As Object, ByVal e As EventArgs)

这两个事件具有同样的参数结构,因此他们的委托类型应当是一样的。但是VB还没有智能到那个地步,这两句语句将产生两个不同的委托类型。更深一步,这种参数的处理过程已经被系统定义为System.EventHandler,所以没必要创建任何新的委托类型,只需要这样写:

Public Event Test As EventHandler
Public Event Test1 As EventHandler

只有一种情况可以随意使用参数表形式定义事件,那就是实现定义在接口中的事件时。比如接口中有这个事件:

Public Interface ITest
    Event Test As EventHandler
End Interface

则实现这个接口的事件定义可以写成

Public Event Test(ByVal sender As Object, ByVal e As EventArgs) Implements ITest.Test

这里不会产生新的委托类型,因为VB能够判断事件的委托类型来自于接口中的定义。用显示指定委托类型的方式定义事件的另一个好处是让C#中使用该事件的代码变得简洁些。

在引发事件方面,很多VB的初学者到处随意使用RaiseEvent来引发事件,这是非常不正确的。应该总是在一个叫OnXXX(XXX是事件的名称)的受保护的虚方法中引发事件。比如引发上面例子中Test事件,应该写一个这样的方法:

Protected Overridable Sub OnTest(e As EventArgs)
    RaiseEvent Test(Me, e)
End Sub

在真正需要引发事件的地方调用OnTest方法来引发事件。为什么要这么做?因为只有这样,才能允许该类型的子类控制该事件的行为。如果不是这样写,子类就无法获知父类何时引发事件,更无法在事件引发的时候执行自定义的代码。至于为什么必须起名为OnXXX,这可能是.NET类库一致的约定。请一定要习惯这种写法,在进一步的开发中,我们将逐渐发现这种写法的思想所在。

二、事件参数的设计

我们看到.NET类库中对象的事件总是提供一个Object类型的参数sender和一个XXXEventArgs类型的参数e。这是整个.NET Framework统一的设计。XXXEventArgs是一个继承自System.EventArgs的类型,他表示通过事件传递的数据,而sender则表示事件的引发者。我们设计事件处理方法的时候,如果没有特殊情况,应当总是提供两个参数:一个Sender参数和一个继承自EventArgs的类型的参数。这种设计可以最大限度的让所有事件处理过程有相似的签名。你将会发现,当你的设计需求发生改变时,这种设计可以最大限度地减小整个系统的修改。比如若事件设计成这样:

Event MyEvent(sender As Object, index As Integer, product As String)

当需求改变,需要再多传递一个Decimal类型的price参数时,系统中所有响应这个事件的过程都需要增加一个参数,那将是一场灾难。如果将所有要传递的数据封装在一个继承自EventArgs的类型——MyEventArgs中,代码就能写成这样:

Event MyEvent(sender As Object, e As MyEventArgs)

那么当需求改变的时候,只需要修改一下MyEventArgs的成员即可,响应事件的方法定义不需要做任何变化。
多数情况下,事件是不需要传递参数的。因此用系统定义的System.EventHandler是非常好的选择,没必要为每个事件编写一个新的EventHandler或者EventArgs类型。

如果需要通过事件进行参数回传(就是说,事件的处理方法要将某些数据传递给事件的引发者),那么同样可以用处理方法的参数来完成,只需要将自定义EventArgs类型中需要回传的属性设置为可写(提供Set访问器)即可。一般不需要用某些教材中建议的那样,设计ByRef的参数来实现回传。因为EventArgs能够满足大部分设计的需要。

至于Sender参数,应该始终让调用者传递Me变量以便事件的处理方法能够正确识别事件的引发者。

结论

事件设计是面向对象的重要内容,设计一个良好的事件通常包括:显式指定事件处理方法的委托类型,一般情况采用系统设定的System.EventHandler,用名为OnXXX的受保护的虚方法引发事件以及用自定义的继承自EventArgs的类型包装事件传递的数据。

原创粉丝点击