VB.net是怎样做到的(三)——Handles和WithEvents
VB除了可以用C#那样的方法来处理事件响应以外,还有从VB5继承下来的独特的事件处理方式——WithEvents。
我喜欢称这种事件处理方式为静态的事件处理,书写响应事件的方法时就已经决定该方法响应的是哪一个事件,而C#则是在代码中绑定事件的。比如下面这个最简单的例子:
Public Class HandlerClass
Public WithEvents MyObj As EventClass
Private Sub MyObj_MyEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyObj.MyEvent
MsgBox("hello")
End Sub
Public Sub New()
MyObj = New EventClass
End Sub
End Class
代码中用到的EventClass是这样的:
Public Class EventClass
Public Event MyEvent As EventHandler
Protected Overridable Sub OnMyEvent(ByVal e As EventArgs)
RaiseEvent MyEvent(Me, e)
End Sub
Public Sub Test()
OnMyEvent(New EventArgs)
End Sub
End Class
我们来复习一下,这段代码隐式地给EventClass编写了两个方法——Add_MyEvent(EventHandler)和Remove_MyEvent(EventHandler),实际上任何使用事件的上下文都是通过调用这两个方法来绑定事件和解除绑定的。C#还允许你书写自己的事件绑定/解除绑定的代码。
那么WithEvents是怎么工作的呢?VB.net的编译器在编译时自动将
Public WithEvents MyObj As EventClass
翻译成下面这个过程:
Private _MyObj As EventClass
Public Property MyObj() As EventClass
Get
Return _MyObj
End Get
Set(ByVal Value As EventClass)
If Not (Me._MyObj Is Nothing) Then
RemoveHandler _MyObj.MyEvent, New EventHandler(AddressOf MyObj_MyEvent)
End If
Me._MyObj = Value
If Me._MyObj Is Nothing Then Exit Property
AddHandler _MyObj.MyEvent, New EventHandler(AddressOf MyObj_MyEvent)
End Set
End Property
由此可见,当对WithEvents变量赋值的时候,会自动触发这个属性以绑定事件。我们所用的大部分事件响应都是1对1的,即一个过程响应一个事件,所以这种WithEvents静态方法是非常有用的,它可以显著增强代码可读性,同时也让VB.net中的事件处理非常方便,不像C#那样离开了窗体设计器就必须手工绑定事件。
不过在分析这段IL的时候,我也发现了VB.net在翻译时小小的问题,就是ldarg.0出现得过多,这是频繁使用Me或this的表现,所以我们在编码过程中一定要注意,除了使用到Me/this本身引用以外,使用它的成员时不要带上Me/this,比如Me.MyInt = 1就改成MyInt = 1,这样的小习惯会为你带来很大的性能收益。
今天在www.aspx.cn看到了一篇帖子,看到wyhw大侠真的希望dotnet blog成为.NET的精品技术Blog。所以我也不能偷懒了,赶快学习,并且多发技术帖子。
回到正题来,我今天在Visual Basic社区上看到在Visual Basic 2005中将加入一个新的运算符——TryCast,相当于C#的as运算符。我一直希望VB有这样一个运算符,既然今天看到了,就顺便研究一下VB和C#的类型转换。先说VB,类型转换运算符主要有CType和DirectCast。他们的用法几乎一样。我详细比较了一下这两个运算符,得出以下结论:
1、在转换成引用类型时,两者没有什么区别,都是直接调用castclass指令,除非重载了类型转换运算符(当然VB.NET还不行,但我没有VB 2005,所以没法试验)。
2、转换成值类型时,CType会调用VB指定的类型转换函数(如果有的话),比如将String转换为Int32时,就会自动调用VisualBasic.CompilerServices.IntegerType.FromString,而将Object转换为Int32则会调用FromObject。其他数值类型转换为Int32时,CType也会调用类型本身的转换方法实施转换。DirectCast运算符则很简单,直接将对象拆箱成所需类型。
所以在用于值类型时,CType没有DirectCast快速但可以支持更多的转换。在C#中,类型转换则为(type)运算符和as运算符。(type)运算符的工作方式与VB的DirectCast很相似,也是直接拆箱或castclass的,但是如果遇到支持的类型转换(如long到int),(type)运算符也会调用相应的转换方法,但不支持从String到int的转换。C#另一个运算符as则更加智能,它只要判断对象的运行实例能否转成目标类型,然后就可以省略castclass指令,直接按已知类型进行操作,而且编译器还可以自动对as进行优化,比如节省一个对象引用等。所以在将Object转换成所需的类型时,as是最佳选择。
由于as有很多优点,Visual Basic 2005将这一特性吸收了过来,用TryCast运算符就可以获得和as一样的效果,而且语法与DirectCast或CType一样。