避免重复注册事件处理程序基本上有两种模式:
(根据这个讨论: C#模式防止事件处理程序挂钩两次)
使用System.Linq命名空间,并通过调用检查事件处理程序是否已注册 GetInvocationList().Contains(MyEventHandlerMethod);
在注册之前取消注册,如下所示:
MyEvent -= MyEventHandlerMethod;
MyEvent += MyEventHandlerMethod;
我的问题是,性能方面,哪个更好,或者它们在性能方面有显着差异?
根据 文件,调用列表存储为数组或类似的东西,并且也存储事件处理程序的顺序。可能存在内部结构以保持快速搜索特定方法。
所以在最糟糕的情况下操作 GetInvocationList().Contains(MyEventHandlerMethod);
是 O(1)
(因为我们只是获得了数组的引用)+ O(n)
搜索方法,即使没有优化。我严重怀疑这是真的,我认为有一些优化代码,它是 O(log_n)
。
第二种方法有额外的添加操作,我认为, O(1)
,因为我们将事件处理程序添加到结尾。
因此,要了解这些操作之间的区别,您需要大量的事件处理程序。
但!如果你使用 第二 正如我所说, 您将把事件处理程序添加到队列的末尾,在某些情况下可能是错误的。所以使用第一个,毫无疑问。
我不认为这对假设的性能增益和实际差异都很重要。
都 GetInvocationList
和 -=
走内部阵列 _invocationList
。 (看到 资源)
LINQ扩展方法 Contains
将需要更多的时间,因为它需要遍历和转换,返回然后检查整个数组 Contains
本身。该 Contains
具有不需要添加事件处理程序的优点 如果 它存在意味着一些性能增益。
- 不适用于外部呼叫者,效率不高 无论如何
- 应该没问题(请注意,每次创建2个委托实例),但也要考虑
- 在大多数情况下,应该很容易知道您是否已经订阅;如果你不知道,那表明一个架构问题
典型的用法是“订阅{某些用法} [取消订阅]”,其中取消订阅可能没有必要,具体取决于活动发布者和订阅者的相对生命周期;如果你真的有一个可重入的场景,那么“订阅,如果还没有订阅”本身就有问题,因为什么时候 以后取消订阅,你不知道你是否阻止了外部迭代接收事件。
MyEvent -= MyEventHandlerMethod
首先需要在调用列表中找到已注册的事件处理程序才能将其删除。
所以 GetInvocationList().Contains
更好,但它确实无足轻重。
但是,请注意您无法访问 event EventHandler foo
的调用清单....