代码之家  ›  专栏  ›  技术社区  ›  M.A. Hanin

自定义事件调用列表实现注意事项

  •  1
  • M.A. Hanin  · 技术社区  · 15 年前

    我正在寻找一些关于在VB.NET(VisualStudio2008,.NET3.5)中实现自定义事件的指针。

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

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

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

    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
    
    2 回复  |  直到 15 年前
        1
  •  3
  •   Marc Gravell    15 年前

    我会说使用简单的委托;很简单-就是这样 已经 List<T> . 您还有额外的列表对象和数组的开销,它们可能为空,因此对垃圾收集等影响更大。

    另外,如果你正在做 对此,考虑一下 EventHandlerList ,以提供对 稀疏 事件-也就是说,你想公开很多事件,但其中许多可以取消分配。

    第一个示例更接近于“标准”事件(尽管您可能希望在调用 Invoke 必须是线程安全的,这样可能会有过多的杀伤力。

    编辑 同时也发生了 功能性

    • 委托方法将具有相同目标方法/实例的不同实例视为相等的(我不认为 列表<T> (遗嘱)
    • 复合:add A、add B和remove(A+B)-对于委托,这将产生null/empty,但是list方法将保留A和B(对于(A+B)remove,它找不到任何内容)
        2
  •  0
  •   supercat    14 年前

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

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

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

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

    推荐文章