事件是你的代码兵器库中的主要部分,无论你用Visual Basic? 6.0,Visual Basic .NET 2002,Visual Basic .NET 2003,还是Visual Basic 2005 。
Visual Basic中的自定义事件
还记得在一个事件有多个侦听器时发生的问题,还有一个事件侦听器抛出一个异常吗?这个问题有一个相当简单的解决方案,用多路广播委托实例的回调列表关联到这个事件。然而,在Visual Basic .NET 2002 和2003,这里有一些其他的事件挑战:(如果)没有复杂或低效率的代码,简单(的方法)将无法体现。Visual Basic 2005以前,事件委托类型的实例总是由Visual Basic编译器为你创建,并且编译器无法提供给你修改这个委托实例的行为。
Visual Basic 2005为事件声明添加了新的Custom关键字。这个关键字允许你为事件的AddHandler, RemoveHandler和RaiseEvent行为提供代码。 这取决于你创建适当的委托类型及创建拥有关于事件侦听器信息的类型的实例。然而,除此之外,你有对你如何处理事件有着完全地控制。
为了在Visual Studio 2005中创建一个自定义事件,在一个类里面放入你的游标,并为你的事件录入一个声明,如下:
Public Custom Event <YourEventName> As <EventDelegateType> |
当你完成这行代码时,编辑器将插入关联的AddHandler, RemoveHandler和RaiseEvent部分。比如,设想一下,你希望通过创建一个自定义事件处理程序来解决异常问题。示例项目的Visual Basic 2005版的FileSearch6类包含像这样做的一个自定义FileFound事件。代码包括适当的事件委托的一个声明,如下所示:
Public Delegate Sub FileFoundEventHandler( _ ByVal sender As Object, ByVal e As FileFoundEventArgs) |
键入事件声明添加一个空的自定义事件,如图 10 所示的。
Public Custom Event FileFound As FileFoundEventHandler AddHandler(ByVal value As FileFoundEventHandler)
End AddHandler
RemoveHandler(ByVal value As FileFoundEventHandler)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As FileFoundEventArgs)
End RaiseEvent End Event |
这要由你提供事件委托实例的存储(storage),并提供储存,移出并回调事件侦听器的代码。对于本例,因为代码需要能单独回调每个侦听器并捕捉及处理任何异常,FileSearch6类包括了一个储存了FileFoundEventHandler实例的泛型列表集合(generic List collection)。每次任何类为这个事件调用AddHandler,或用一个Handles子句捕捉这个事件,Visual Basic运行时引擎调用FileFound 自定义事件的AddHandler部分。代码必须添加FileFoundEventHandler以传递到泛型列表。RemoveHandler部分从内部集合里移去指定的委托实例。RaiseEvent部分调用每个委托实例的Invoke方法,捕捉和处理发生的异常。完全的自定义事件看起来如图 11 所示的代码。
Private listeners As New List(Of FileFoundEventHandler) Public Custom Event FileFound As FileFoundEventHandler AddHandler(ByVal value As FileFoundEventHandler) listeners.Add(value) End AddHandler
RemoveHandler(ByVal value As FileFoundEventHandler) If listeners.Contains(value) Then listeners.Remove(value) End If End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As FileFoundEventArgs) For Each listener As FileFoundEventHandler In listeners Try listener.Invoke(sender, e) Catch ex As Exception ’ Something goes wrong? Just move on to the next handler. End Try Next End RaiseEvent End Event
|
通过推进代码到事件声明本身,引发事件的代码不再需要担心处理异常的问题了。这就是说,与使用你前面看到的代码来引发事件不同,代码假定在Visual Basic 2005中的FileSearch6类可以简单地调用OnFileFound方法(它来引发事件)或直接调用RaiseEvent。担心异常的责任现在适得其所:它就在事件代码本身之中。这项技术直到Visual Basic 2005才可以使用。注意Visual Studio 2005现仍在beta版(测试中)。正因为如此,到最终版本发布前其细节会有变化。
还有其它可以用到自定义事件吗?Rocky Lhotka,一个Visual Basic MVP,在他的blog上包括另一个详细的例子( .NET 2.0 solution to serialization of objects that raise events )。他论述了 你可能会用到这个技术来解决涉及引发事件类序列化而侦听器没有序列化的问题。(令人惊讶的是,这经常发生,因为窗体没有序列化,但是常常用户创建事件的侦听器是序列化的。)Paul Vick,作为Microsoft的Visual Basic开发团队的成员之一,他的blog上包括一个例子显示你如何可以使用一个 自定义事件来减少暴露给大量事件而只有很少的事件可能会用到的类的系统开销。对于窗体来说就是这个情况,例如—窗体类暴露于大量事件,但是大多数时间里,你只会处理它们中一或两个。没有某些技巧,编译器将为每个事件引发一个委托实例,尽管你不会使用它们。请看Paul的blog在 Custom events。
查看本文来源