VB.NET - 将事件作为参数传递

时间:2010-11-10 09:01:04

标签: vb.net events parameters

我需要将一个事件作为参数传递给一个函数。有没有办法做到这一点?

原因是我有两行代码的序列遍布我的程序,在那里我动态地删除事件的处理程序,然后再次设置处理程序。我正在为几个不同的事件和事件处理程序执行此操作,因此我决定编写一个执行此操作的函数。

作为一个例子,假设我的代码中有一个名为combobox1的组合框,我有一个名为indexChangedHandler的处理程序。在我的代码的几个地方,我有以下两行:

RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler

现在,我不想继续在我的程序中重复上面两行代码(或类似代码),所以我正在寻找一种方法:

Private Sub setHandler(evt As Event, hndler As eventhandler)
     RemoveHandler evt, hndler
     AddHandler evt, hndler
End Sub

所以在我的程序中出现这两行代码(或类似代码)的地方,我可以用以下代码替换它们:

setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler)

到目前为止,setHandler函数参数的“evt as Event”部分给出了错误。

P.S:我在其他几个论坛上问了这个问题并不断被问到为什么我想在删除后立即设置处理程序。原因是因为动态添加事件处理程序n次会导致处理程序在事件发生时执行n次。为了避免这种情况,也就是说,为了确保在事件发生时只执行一次处理程序,每次我想动态添加处理程序时,我首先删除处理程序。

你可能会问为什么处理程序会首先被添加几次......原因是因为我在我的表单中发生了一个特定的事件,比如E1之后才添加处理程序(我在其中添加了处理程序)事件E1的处理程序。事件E1可以在我的表单中多次出现。如果我在每次再添加之前都不删除处理程序,则会添加处理程序并因此执行多次。

无论如何,函数中发生的处理对我来说并不是最重要的,而只是将事件作为参数传递的方法。

3 个答案:

答案 0 :(得分:12)

当然你可以传递事件......你可以通过Action(Of EventHandler)来做你想做的事。

如果您有一个定义类似事件的类:

Public Class ComboBox
    Public Event SelectedIndexChanged As EventHandler   
End Class

根据ComboBox的实例,您可以创建添加&删除处理程序操作,如下所示:

Dim combobox1 = New ComboBox()

Dim ah As Action(Of EventHandler)
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As Action(Of EventHandler)
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h

(现在,这是VB.NET 4.0代码,但是您可以使用AddressOf在3.5中执行此操作,稍微更麻烦一点。)

所以如果我有一个处理程序Foo

Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs)
    Console.WriteLine("Over here with Foo!")
End Sub

Raise上的ComboBox方法我现在可以这样做了:

ah(AddressOf Foo)
combobox1.Raise()
rh(AddressOf Foo)

这会写消息“Over here with Foo!”如预期的那样。

我也可以创建这个方法:

Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler))
    h(AddressOf Foo)
End Sub

我可以像这样传递事件处理程序操作:

PassActionOfEventHandler(ah)
combobox1.Raise()
PassActionOfEventHandler(rh)

再次写下消息“Over here with Foo!”。

现在,一个可能存在问题的问题是,您可能会意外地在代码中交换添加和删除事件处理程序委托 - 毕竟它们是相同的类型。因此,很容易为添加和删除操作定义强类型委​​托,如下所示:

Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T)
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T)

除委托类型外,定义委托实例的代码不会改变:

Dim ah As AddHandlerDelegate(Of EventHandler)
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As RemoveHandlerDelegate(Of EventHandler)
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h

所以这现在让你非常有创意。您可以定义一个函数,该函数将使用添加处理程序委托,删除处理程序委托和事件处理程序,它将连接一个事件处理程序,然后返回一个IDisposable,您可以稍后使用它来删除处理程序,无需保留对事件处理程序的引用。这对于使用Using语句非常方便。

这是签名:

Function SubscribeToEventHandler(
    ByVal h as EventHandler,
    ByVal ah As AddHandlerDelegate(Of EventHandler),
    ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable

所以给定这个功能我现在可以这样做:

combobox1.Raise()
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh)
    combobox1.Raise()
    combobox1.Raise()
End Using
combobox1.Raise()

这会写下消息“Over here with Foo!”只有两次。第一个和最后一个Raise调用不在对事件处理程序的订阅范围内。

享受!

答案 1 :(得分:3)

你无法传递事件。 Event不是类型,它是定义一对方法的关键字,添加和删除,用于更改类的事件成员的状态。它们非常像这方面的属性(有获取和设置方法)。

与属性一样,添加和删除方法可以执行任何操作。通常情况下,这些只会维护一个委托实例,它本身就是一个MulticastDelegate,换句话说,就是一个代理列表,如果事件被引发则逐个调用它们。

您可以在C#中清楚地看到此结构,但未使用AddHandler / RemoveHandler进行屏蔽,但您可以直接编辑关联处理程序列表:myObject.Event += new Delegate(...);

而且,再次像属性一样,作为实际抽象两种不同方法的类的成员,不可能将事件作为对象传递。

答案 2 :(得分:2)

“事件”一词有许多不同的含义,具体取决于具体情况。在您正在寻找的上下文中,Event是一对匹配的方法,可以有效地添加或删除订阅列表中的委托。不幸的是,VB.NET中没有类来表示没有附加对象的方法地址。有人可能会使用reflection来获取适当的方法,然后将这些方法传递给一个可以同时调用它们的例程,但在你的特殊情况下可能会很愚蠢。

我可以看到在你描述的意义上传递事件可能有用的唯一情况是类似于IDisposable对象,它订阅来自较长寿命对象的事件,并且需要处置时取消订阅所有事件。在这种情况下,使用反射为每个事件获取remove-delegate方法,然后在处理对象时调用所有remove-delegate方法可能会有所帮助。不幸的是,我知道无法表示要检索的事件,除了字符串文字,其有效性直到运行时才能检查。