这是怎么回事
WithEvents
是.NET Framework本身不支持的功能。VB.NET正在.NET之上实现它。该功能之所以存在,是因为它也是由VB6提供的。不过,该功能在VB6中的实现方式非常不同,因为COM和.NET之间的事件模型存在根本性差异。
我不会详细介绍VB6是如何实现该功能的;这不是真的相关。重要的是事件如何与.NET一起工作。基本上,在.NET中,事件必须显式挂接和取消挂接。定义事件时,属性的定义方式有很多相似之处。特别是,有一个方法将处理程序添加到事件,还有一个方法删除处理程序,类似于属性的“set”和“get”方法之间的对称性。
事件使用这样的方法的原因是对外部调用程序隐藏附加处理程序的列表。如果类外的代码可以访问附加处理程序的完整列表,那么它可能会干扰它,这将是一种非常糟糕的编程实践,可能会导致非常混乱的行为。
VB.NET通过
AddHandler
和
RemoveHandler
操作员。在C#中,使用
+=
和
-=
运算符,其中左侧参数是事件成员引用。
什么
以事件方式
给你的是隐藏
AddHandler
和
移除处理器
电话。重要的是要认识到
还在吗
而且,它们只是隐含的。
因此,当您编写这样的代码时:
Private WithEvents _obj As ClassWithEvents
Private Sub _obj_GronkulatedEvent() Handles _obj.GronkulatedEvent
...
End Sub
…您要求VB.NET确保:
分配给的任何对象
_obj
(请记住,您可以随时更改该对象引用),事件
GronkulatedEvent
应该这样处理
Sub
.如果更改引用,则旧对象的
怪诞事件
应立即分离,并且新对象的
怪诞事件
附属的。
VB.NET通过将字段转换为属性来实现这一点。添加
以事件方式
意味着这个领域
_目标
(或者,在您的情况下,
_item
)是
实际上不是一个领域
.创建一个秘密支持字段,然后
_项目
成为一个属性,其实现如下所示:
Private __item As ItemViewModel ' Notice this, the actual field, has two underscores
Private Property _item As ItemViewModel
<CompilerGenerated>
Get
Return __item
End Get
<CompilerGenerated, MethodImpl(Synchronized)>
Set(value As ItemViewModel)
Dim previousValue As ItemViewModel = __item
If previousValue IsNot Nothing Then
RemoveHandler previousValue.GronkulatedEvent, AddressOf _item_GronkulatedEvent
End If
__item = value
If value IsNot Nothing Then
AddHandler value.GronkulatedEvent, AddressOf _item_GronkulatedEvent
End If
End Set
End Property
那么,为什么这会导致你看到的“滞后”?嗯,您不能传递属性“ByRef”。要传递“ByRef”,您需要知道它的内存地址,但属性将内存地址隐藏在“get”和“set”方法后面。在C#这样的语言中,您只会得到一个编译时错误:属性不是L值,因此无法传递对它的引用。然而,VB.NET更宽容,在幕后编写额外的代码,使事情为您服务。
在您的代码中,您传递的是
看起来像
一个领域
_项目
成员,进入
SetProperty
,它接受参数
ByRef
所以它可以写一个新值。但是,由于
以事件方式
这个
_项目
成员实际上是一种财产。那么,VB.NET做什么呢?它为调用创建一个临时局部变量
集合属性
,然后在调用后将其分配回属性:
Public Property Item As ItemViewModel
Get
Return _item ' This is actually a property returning another property -- two levels of properties wrapping the actual underlying field -- but VB.NET hides this from you
End Get
Set
' You wrote: SetProperty(_item, value)
' But the actual code emitted by the compiler is:
Dim temporaryLocal As ItemViewModel = _item ' Read from the property -- a call to its Get method
SetProperty(temporaryLocal, value) ' SetProperty gets the memory address of the local, so when it makes the assignment, it is actually writing to this local variable, not to the underlying property
_item = temporaryLocal ' Once SetProperty returns, this extra "glue" code passes the value back off to the property, calling its Set method
End Set
End Property
所以,因为
以事件方式
将字段转换为属性,VB.NET必须将对属性的实际赋值推迟到调用
集合属性
返回。
希望这有道理!:-)