哪种C#模式具有更好的性能以避免重复的事件处理程序?

避免重复注册事件处理程序基本上有两种模式:(根据此讨论: C#模式以防止事件处理程序挂钩两次 )

  1. 使用System.Linq命名空间,并通过调用GetInvocationList().Contains(MyEventHandlerMethod);检查事件处理程序是否已注册GetInvocationList().Contains(MyEventHandlerMethod);

  2. 在注册之前取消注册,如下所示:

     MyEvent -= MyEventHandlerMethod; MyEvent += MyEventHandlerMethod; 

我的问题是,性能方面,哪个更好,或者它们在性能方面有显着差异?

根据文档 ,调用列表存储为数组或类似的东西,并且也存储事件处理程序的顺序。 可能存在内部结构以保持快速搜索特定方法。

所以在最坏的情况下GetInvocationList().Contains(MyEventHandlerMethod);O(1) (因为我们只是获得了数组的引用)+ O(n)用于搜索方法,即使它没有优化。 我严重怀疑这是真的,我认为有一些优化代码,它是O(log_n)

第二种方法有额外的添加操作,我认为是O(1) ,因为我们将事件处理程序添加到结尾。

因此,要了解这些操作之间的区别,您需要大量的事件处理程序。
但是 ! 如果您使用第二种方法,正如我所说, 您将把事件处理程序添加到队列的末尾,在某些情况下这可能是错误的。 所以使用第一个,毫无疑问。

我不认为这对假设的性能增益和实际差异都很重要。

GetInvocationList-=遍历内部数组_invocationList 。 (见来源 )

LINQ扩展方法Contains将花费更多时间,因为它需要遍历并转换,返回然后由Contains本身检查整个数组。 Contains的优点是, 如果它存在,则不需要添加事件处理程序,这意味着一些性能提升。

  1. 不适用于外部呼叫者, 无论如何都不是很有效
  2. 应该没问题(请注意,每次创建2个委托实例),但也要考虑
  3. 在大多数情况下,应该很容易知道您是否已经订阅; 如果你不知道,那表明一个架构问题

典型的用法是“订阅{某些用法} [取消订阅]”,其中取消订阅可能没有必要,具体取决于活动发布者和订阅者的相对生命周期; 如果你实际上有一个可重入的场景,那么“订阅即使尚未订阅”本身也是有问题的,因为在以后取消订阅时 ,你不知道你是否阻止了外部迭代接收事件。

MyEvent -= MyEventHandlerMethod首先需要在调用列表中找到已注册的事件处理程序,以便将其删除。 所以GetInvocationList().Contains更好,但它确实无关紧要。

但是,请注意,您无法访问event EventHandler foo的调用列表….