虽然语法与较低版本的 Visual Basic 相同,但是激发事件时所发生的事情则与现在截然不同。 使用 RaiseEvent 关键字激发事件时,Visual Basic .NET 编译器生成执行每个事件处理程序所需的代码。 例如,当您编译以下代码时您认为会出现什么情况?
RaiseEvent LargeWithdraw(Amount)
Visual Basic .NET 编译器将此表达式扩展为在保留多路广播委托对象的私有字段上调用 Invoke 的代码。 换句话说,使用 RaiseEvent 关键字与在以下 snippet 中编写代码具有完全相同的效果:
If (Not LargeWithdrawEvent Is Nothing) Then
LargeWithdrawEvent.Invoke(Amount)
End If
注意,Visual Basic .NET 编译器生成的代码执行检查以确保 LargeWithdrawEvent 字段包含对某个对象的有效引用。 这是因为 LargeWithdrawEvent 字段的值在第一个处理程序方法注册之前一直为 Nothing。 因此,除非当前至少有一个处理程序方法已注册,否则生成的代码并不尝试调用 Invoke。
您应该能够对激发事件进行观察。 使用 RaiseEvent 关键字或者根据编译器自动生成的 LargeWithdrawEvent 私有字段直接进行编程通常并没有什么分别。 两种方法都生成相同的代码:
'*** this code
RaiseEvent LargeWithdraw(Amount)
'*** is the same as this code
If (Not LargeWithdrawEvent Is Nothing) Then
LargeWithdrawEvent.Invoke(Amount)
End If
在很多情况下,您可能喜欢使用 RaiseEvent 关键字语法,因为它需要的键入较少,生成的代码较简洁。 但是,在某些情况下,当您需要较多控制时,根据 LargeWithdrawEvent 私有字段进行明确编程可能会有意义。 让我们看一个这种情况的示例。
想象以下情况:BankAccount 对象有三个事件处理程序已注册以接收 LargeWithdraw 事件的通知。 如果使用 RaiseEvent 关键字触发事件并且调用列表中的第二个事件处理程序出现异常,将会出现什么情况? 包含 RaiseEvent 语句的代码行将接收运行时异常,但是您可能没办法确定哪个事件处理程序导致异常。 而且,可能没有办法处理第二个事件处理程序导致的异常,也没有办法按预期方式在执行第三个事件处理程序的位置继续进行。
但是,如果您愿意根据 LargeWithdrawEvent 私有字段进行编程,则可以更适当的方式处理事件处理程序导致的异常。 检查图 3 中的代码。 正如您所看到的,降至一个较低的级别并根据该私有委托字段进行编程可以提供额外程度的控制。 您可以恰当地处理异常,然后继续执行较晚出现在列表中的事件处理程序。 与 RaiseEvent 语法相比,该方法具有明显的好处,在该方法中一个事件处理程序导致的异常将阻止执行较晚出现在调用列表中的任何事件处理程序。
创建和注册事件处理程序
现在,您已经知道如何定义和激发事件,该是讨论如何创建事件处理程序并在给定源中注册它的时候了。 有两种不同的方法可以在 Visual Basic .NET 中完成以上操作。 第一种方法称为动态事件绑定,涉及 AddHandler 关键字的使用。 第二种方法称为静态事件绑定,涉及熟悉的 Visual Basic 关键字 WithEvents 的使用。 我打算在下一期讨论静态事件绑定。 所以现在,让我们来看一看动态事件绑定的工作原理。
请记住,事件处理程序是一个委托对象。 因此,可以通过从事件所基于的委托类型实例化一个委托对象,创建一个事件处理程序。 创建此委托对象时,必须将其绑定到要成为事件处理程序的目标处理程序方法。
创建事件处理程序后,必须通过在事件源上调用特定的注册方法在特定的事件中注册它。 回忆一下,LargeWithdraw 事件的注册方法名为 add_LargeWithdraw。 当您调用 add_LargeWithdraw 方法并将委托对象作为参数传递时,事件源将委托对象添加到将接收事件通知的事件处理程序列表中。
有关事件注册会出现混淆的是您从未直接调用 add_LargeWithdraw 等方法。 实际上,如果您按名称访问事件注册方法,Visual Basic .NET 编译器将生成编译时错误。 但是,您可以使用包括 AddHandler 语句的替代语法。 当您使用 AddHandler 语句时,Visual Basic .NET 编译器生成为您调用事件注册方法的代码。
让我们来看一个使用动态事件注册绑定几个事件处理程序的示例。 想象您已经在 AccountHandlers 类中编写了以下共享方法的集合:
Class AccountHandlers
Shared Sub LogWithdraw(ByVal Amount As Decimal)
'*** write withdrawal info to log file
End Sub
Shared Sub GetApproval(ByVal Amount As Decimal)
'*** block until manager approval
End Sub
End Class
如果要将这些方法用作 BankAccount 类的 LargeWithdraw 事件的事件处理程序,您应该做什么? 让我们从创建绑定到处理程序 LogWithdraw 的事件处理程序开始。 首先,您必须创建将成为事件处理程序的委托对象:
Dim handler1 As LargeWithdrawHandler
handler1 = AddressOf AccountHandlers.LogWithdraw
然后,您必须使用 AddHandler 语句在事件源中注册该新的委托对象。 当您使用 AddHandler 语句注册事件处理程序时,您需要传递两个参数,类似以下内容:
AddHandler <event>, <delegate object>
AddHandler 需要的第一个参数是对类或对象的事件进行求值的表达式。 第二个参数是对将被绑定为事件处理程序的委托对象的引用。 下面是使用 AddHandler 语句在 BankAccount 对象的 LargeWithdraw 事件中注册事件处理程序的一个示例:
'*** create bank account object
Dim account1 As New BankAccount()
'*** create and register event handler
Dim handler1 As LargeWithdrawHandler
handler1 = AddressOf AccountHandlers.LogWithdraw
AddHandler account1.LargeWithdraw, handler1
当您使用 AddHandler 关键字注册 LargeWithdraw 事件的事件处理程序时,Visual Basic .NET 编译器将扩展此代码以调用注册方法 add_LargeWithdraw。 执行包含 AddHandler 语句的代码后,您的事件处理程序已就位,并已准备就绪可以进行通知。 因此,无论任何时候 BankAccount 对象激发 LargeWithdraw 事件时,都将执行 LogWithdraw 方法。
在上一示例中,我使用了较长形式的语法以便确切地说明创建和注册事件处理程序时所发生的事情。 但是,明白了原理之后,您可能希望使用更简洁的语法来实现同样的目标,如下所示:
'*** create bank account object
Dim account1 As New BankAccount()
'*** register event handlers
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.GetApproval
由于 AddHandler 语句期望将委托对象作为第二个参数引用,因此您可以使用 AddressOf 运算符的速记语法,后跟目标处理程序方法。 当 Visual Basic .NET 编译器发现这种情况后,它就会生成额外的代码以创建将成为事件处理程序的委托对象。
Visual Basic .NET 语言的 AddHandler 语句由 RemoveHandler 语句补充。 RemoveHandler 需要的两个参数与 AddHandler 相同,但是它具有相反的效果。 它通过调用事件源提供的 remove_LargeWithdraw 方法从已注册处理程序列表中删除目标处理程序方法:
Dim account1 As New BankAccount()
'*** register event handler
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw
'*** unregister event handler
RemoveHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw
现在,您已经看到了使用事件实现回调设计所需的所有步骤。上面的代码显示了一个完整的应用程序,在该应用程序中两个事件处理程序已注册以接收来自 BankAccount 对象的 LargeWithdraw 事件的回调通知。
小结
虽然使用事件的动机和某些语法与较低版本的 Visual Basic 相比都没有变,但是,您必须承认现在情况有些不同了。 正如您所看到的,您对如何响应事件的控制能力比以前更强了。 如果您希望降低级别并根据委托进行编程,则更是如此。
在下一期的 Basic Instincts 栏目中,我打算继续有关事件的此讨论。 我将向您说明 Visual Basic .NET 如何通过您熟悉的 WithEvents 关键字语法支持静态事件绑定,并将讨论 Handles 子句。 要真正控制事件,您必须能够轻松驾驭动态事件注册和静态事件注册。
相关推荐
ASP.NET3.5 AJAX客户端编程精选166例(使用C#)
ASP.NET MVC 5高级编程 第5版.part1,有文件较大,进行压缩分割,需要下载其他部part2,part3分
北大青鸟 s2 深入.net平台和C#编程ATM编程
ASP.NET MVC4 Web 编程,增加了pdf目录标签
asp.net web forms高级编程asp.net web forms高级编程
ASP.NET MVC 3 高级编程中文完整版,带目录书签PDF
深入.NET平台和C#编程.NET平台和C#编程4~9章PPT课件
ASP.NET MVC5高级编程(第5版.NET开发经典名著)作为Microsoft备受欢迎的MVC技术的最新版本,MVC 5是一个成熟的Web应用程序框架,支持快速的、TDD友好的开发。MVC允许开发人员创建动态的、数据驱动的网站。这样的...
asp.net mvc4
ASP.NET MVC4 Web编程完整版PDF教程ASP.NET MVC4 Web编程完整版PDF教程ASP.NET MVC4 Web编程完整版PDF教程ASP.NET MVC4 Web编程完整版PDF教程
asp.net MVC5 高级编程,中文版本, PDF格式, 学习MVC相当不错!
ASP.NET MVC 5高级编程(第5版)PDF电子书(清华大学出版社),ASP.NET MVC 5高级编程是非常实用的一个适合没有任何编程基础朋友们学习的教程文档,里面包含了非常完整和详细的编程教学内容,可以让你从零基础的小白...
北大青鸟《深入.NET平台和C#编程(PPT+源码)》【第二章】
asp.net xml高级编程 c#编程篇asp.net xml高级编程 c#编程篇
.net自定义事件编程 有多种方式可以实现
MVC专家“梦之队”对ASP.NET MVC 4的全新诠释 由Microsoft专家和极受敬重的软件开发社区负责人撰写的《ASP.NET MVC 4高级编程(第4版)》将带您学习最前沿的Web框架:ASP.NET MVC 4。本书开篇简要介绍ASP.NET MVC框架...
S2-1-深入.NET平台和C#编程(PPT+源码)【第十一章】
深入.NET平台和C#编程第七章.rar
深入.NET平台和C#编程(.NETOOP)第一章-第九章
其实,不管是使用哪种具体的技术来针对Office进行开发(比如VSTO,或者用C#编写一个Office Add-in,或者在一个WinForms程序中调用Office的功能,甚至在一个ASP.NET应用的服务器端启动一个Excel进程),只要是基于...