1

我正在寻找一些关于在 VB.NET(Visual Studio 2008、.NET 3.5)中实现自定义事件的指针。

我知道“常规”(非自定义)事件实际上是委托,所以我在实现自定义事件时考虑使用委托。另一方面,Andrew Troelsen“Pro VB 2008 和 .NET 3.5 平台”一书在他的所有自定义事件示例中都使用了集合类型,而微软的示例代码符合这一思路。

所以我的问题是:在选择一种设计而不是另一种设计时,我应该考虑哪些因素?每种设计的优缺点是什么?其中哪一个类似于“常规”事件的内部实现?

下面是演示这两种设计的示例代码。

Public Class SomeClass
    Private _SomeEventListeners As EventHandler
    Public Custom Event SomeEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Combine(_SomeEventListeners, value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Remove(_SomeEventListeners, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            _SomeEventListeners.Invoke(sender, e)
        End RaiseEvent
    End Event

    Private _OtherEventListeners As New List(Of EventHandler)
    Public Custom Event OtherEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _OtherEventListeners.Add(value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _OtherEventListeners.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            For Each handler In _OtherEventListeners
                handler(sender, e)
            Next
        End RaiseEvent
    End Event
End Class
4

2 回答 2

3

我会说使用简单的委托;简单地说 - 它已经(内部)是一个列表,所以你通过将它包装在重复工作List<T>。您还需要额外的列表对象和数组的开销,这可能是空的,因此对垃圾收集等的影响更大。

此外,如果您正在做很多这样的事情,请考虑,它的存在是为了提供对稀疏EventHandlerList事件的有效访问- 即您想要公开大量事件但其中许多可以未分配的地方。

第一个示例更接近“标准”事件(尽管您可能希望在调用 时注意未分配/空处理程序Invoke,因为它可能为空)。此外,请注意,某些语言(老实说,我不知道 VB 在这里做了什么)将同步应用于事件,但实际上很少有事件真正需要线程安全,因此这可能是矫枉过正。

编辑还发现它们之间存在功能差异:

  • 委托方法将具有相同目标方法/实例的不同实例视为相等(我认为List<T>不会)
  • 重复代表:代表先删除最后一个;列表首先删除最早的
  • 复合:添加 A、添加 B 和删除 (A+B) - 使用委托这应该产生 null / 空,​​但列表方法将保留 A 和 B(使用 (A+B) 删除无法找到任何东西)
于 2010-04-10T10:03:25.477 回答
0

使用 MulticastDelegate 保存订阅事件列表当然是一种可行的方法,但不是我特别热衷的方法。要从 MulticastDelegate 添加或删除事件,必须执行以下两项操作之一:

  1. 获取锁,从旧的委托创建一个新的委托,但添加或删除一个事件,将委托指针设置为该委托,然后释放锁
  2. 复制对旧委托的引用,从中创建新委托,但添加或删除事件,如果旧委托未更改,则使用 Interlocked.CompareExchange 存储对新委托的引用,如果 CompareExchange 失败则重新开始.

如果没有争用,后一种方法可能会提供稍好的性能,但如果许多线程同时尝试添加或删除事件,则可能会表现不佳。但是,后一种方法的一个优点是,在持有锁时没有线程死亡的危险。

这两种方法似乎都不是特别干净。如果计划通过简单地调用委托来调用所有事件,则事件调用性能可能会抵消添加/删除性能。另一方面,如果计划使用 GetInvocationList 以便将事件调用包装在 try/catch 块中,则最好只使用(适当锁定的)列表或其他此类数据结构。

于 2011-03-23T22:58:58.360 回答