EventSubscriptionManager - 不再泄漏事件委托
一个管理已附加事件的解决方案
引言
当您手动将代码中的事件处理程序“订阅”到由控件或任何发布事件的通用类发布的事件时,您就隐式地承担了取消订阅事件的责任。如果您未能这样做,您将为每一个未能取消订阅的事件泄露一个事件委托。
通常,您会在类的构造函数中“订阅”事件,然后在类的析构函数/终结器/Dispose 方法中“取消订阅”。当然,这并不是什么难事……只要您不忘记的话。
此外,如果您的代码中到处都附加了事件,而不仅仅是在构造函数/析构函数中,那会怎么样?即使您记得进行清理,这也可能是一件棘手的事情。
我编写了附加类 EventSubscriptionManager
来解决这个问题。
背景
这里的想法是简化事件订阅的管理。该解决方案包含两个简单的类:
EventSubscriptionManager
,它主要是一个EventSubscription
对象的集合。EventSubscription
,它管理对给定源对象及其事件的单个订阅。
这些类负责订阅事件,然后在您忘记或者压根就不想处理事件取消订阅的情况下,负责取消订阅。
当我决定厌倦了事件订阅管理并下定决心要解决它时,我考虑了几项要求。
- 它必须易于使用。(否则,有什么意义呢?)
- 它必须允许我随时订阅和取消订阅。
- 它必须为任何给定的事件维护多个订阅。
- 它必须支持所有事件委托,即使是那些不继承自
EventHandler
的。 - 它必须负责删除所有事件订阅,无论我何时何地在代码中分配它们。
正常的订阅通常如下所示:
// subscribing to get a method in your class, called MyEventMethod
someObject.SomeEvent += new SomeEventHandler(MyEventMethod);
// or the shorthand way: someObject.SomeEvent += new MyEventMethod;
同样……取消订阅也类似:
// time to unsubscribe to events....
someObject.SomeEvent -= new SomeEventHandler(MyEventMethod);
如您所见,这很简单。事件与委托 101。但是,要做到这一点……嗯……正如我上面所说,那是另一回事了。
Using the Code
使用该代码非常方便。
首先,下载附加的源文件EventSubscriptionManager.cs 并将其添加到您的项目中,或者将其添加到您的“实用工具库”中以供重用。
第二,添加对该类命名空间的引用:
using EventSubscription.Manager;
第三,在您想要管理资源的 Form
或 Class
中添加该类的一个实例成员:
EventSubscriptionManager m_eventMgr = new EventSubscriptionManager();
现在您已准备就绪。使用上面的示例,我们将订阅相同的事件:
// subscribing to get a function called MyEventMethod
m_eventMgr.Subscribe(someObject, "SomeEvent", new SomeEventHandler(MyEventMethod));
同样……取消订阅也类似:
// time to unsubscribe to events....
m_eventMgr.Unsubscribe(someObject, "SomeEvent", new SomeEventHandler(MyEventMethod));
如果您未能取消订阅任何事件,当 m_eventMgr
被销毁时,这会被处理。事实上,根本没有必要考虑管理事件订阅的销毁。这是管理器的职责。
如果您确实希望“删除”所有事件,可能用于重置或重新初始化事件,您可以简单地调用:
m_eventMgr.UnsubscribeAll(); // reset our subscriptions...
就是这样!不再有泄露。不再需要费力跟踪您订阅了什么。
它是如何工作的?
反射来帮忙!
所有事件处理程序都派生自 MulticastDelegate
。所以,这是所有事件处理程序的基类,无论它们是什么。因此,它适用于所有事件和所有事件处理程序。
使用反射,它可以获取指定事件的 EventInfo
,该 EventInfo
包含此对象特定事件的“调用列表”。此列表可能包含对其他事件处理程序的引用。我们当然只对我们自己的感兴趣。
EventInfo eventInfo = eventSource.GetType().GetEvent(m_eventName, c_flags);
从这个类中,我们可以通过以下方式将我们的事件处理程序添加到源事件:
eventInfo.AddEventHandler(eventSource, m_eventHandler);
这会将我们的事件处理程序添加到调用列表中,以及在触发事件时已注册的其他委托。
同样,我们可以通过以下方式调用事件处理程序的取消订阅:
eventInfo.RemoveEventHandler(eventSource, m_eventHandler);
这将从对象事件调用列表中删除我们的委托(至少是第一个实例,如果您有多个实例)。
请注意,示例中未使用 m_eventSource
。这是因为 m_eventSource
已更改为 WeakReference
,以处理源对象可能希望在我们取消订阅之前被垃圾回收的情况。当我们在取消订阅时,eventSource
现在代表我们从 WeakReference m_eventSource
获取的强引用,前提是源对象尚未被垃圾回收。
就是这样。希望您觉得它有用!
历史
已将源对象更改为使用“WeakReference
”。