Introduction
When you manually attach or "subscribe" your code's event handler to an event published by a control or any general class that publishes events, you implicitly assume the responsibility for detaching or "unsubscribing" to the event as well. If you fail to do this, you leak an event delegate for each event you fail to unsubscribe.
Normally you do this, perhaps, "subscribing" to events in the constructor of your class, and perhaps "unsubscribing" from these events in the destructor/finalizer/dispose in your class. This of course is not overwhelming to do...as long as you don't forget.
In addition, what happens if you attach events throughout your code, not just in the constructor/destructor? This can be a tricky thing to do, even if you do remember to do your cleanup.
I wrote the attached class EventSubscriptionManager
to solve this problem.
Background
The idea here is to simplify the management of event subscriptions. The solution here contains two simple classes:
EventSubscriptionManager
which is primarily a collection of EventSubscription
objects.
EventSubscription
which manages a single subscription to a given source object and its event.
These classes are responsible for both subscribing to the event, and then unsubscribing, if you forget or just don't feel like keeping up with event unsubscribing in the first place.
When I decided I was tired of managing event subscriptions and became motivated to do something about it, I had several requirements in mind.
- It has to be easy to use. (otherwise, what's the point right?)
- It had to allow me to subscribe and unsubscribe at will.
- It had to maintain multiple subscriptions for any given event.
- It has to support all event delegates even those not inheriting from
EventHandler
.
- It had to take care of deleting all event subscriptions no matter when and where I allocate them in the code.
Normal subscription is typically done as follows:
someObject.SomeEvent += new SomeEventHandler(MyEventMethod);
Likewise...the unsubscription is done similarly:
someObject.SomeEvent -= new SomeEventHandler(MyEventMethod);
As you can see, there is nothing to it. Events and Delegates 101. But, getting it done... well... as I stated above, that's another story.
Using the Code
Using the code is a snap.
First, download and add the attached source file EventSubscriptionManager.cs to your project, or add it to your "utility library" of reusable code.
Second, add the reference to the namespace of the class:
using EventSubscription.Manager;
Third, add an instance member of the class to the Form
or Class
you want to manage resources:
EventSubscriptionManager m_eventMgr = new EventSubscriptionManager();
Now you are ready. Using the example above, we will subscribe to the same event:
m_eventMgr.Subscribe(someObject, "SomeEvent", new SomeEventHandler(MyEventMethod));
Likewise...the unsubscription is done similarly:
m_eventMgr.Unsubscribe(someObject, "SomeEvent", new SomeEventHandler(MyEventMethod));
If you fail to unsubscribe to any events, this is taken care of when the m_eventMgr
is destroyed. In fact, there is no reason to even consider managing the destruction of the event subscriptions. That's the manager's job.
If you do wish to "remove" all events, possibly to reset events reinitialize, you can, simply call:
m_eventMgr.UnsubscribeAll();
That's it! No more leaks. No more hassle of having to track what you subscribed to.
How Does It Work?
Reflection to the rescue!
All events handlers derive from the MulticastDelegate
. So, this is the base class used for all event handlers no matter what they are. Therefore, it works for all events all event handlers.
Using reflection, it can fetch the EventInfo
for the specified event that contains the "invocation list" for this particular object's particular event. This list may contain references to other event handlers. We, of course, are only interested in ours.
EventInfo eventInfo = eventSource.GetType().GetEvent(m_eventName, c_flags);
From this class, we can add our event handler to the sources event via:
eventInfo.AddEventHandler(eventSource, m_eventHandler);
This adds our event handler to the invocation list along with any other delegates already registered for invocation when the event is fired.
Likewise we can invoke the unsubscribe of the event handler from the event via:
eventInfo.RemoveEventHandler(eventSource, m_eventHandler);
This removes our delegate (at least the first instance if we have more than one) from the object's event invocation list.
Note here that m_eventSource
is not used in the examples. This is because m_eventSource
has been changed to a WeakReference
to deal with the situation where the source object may wish to be collected before we unsubscribe. The eventSource
now represents the strong reference we get from the WeakReference m_eventSource
, if the source object has not been collected at the point we unsubscribe.
And that's all there is to it. I hope you find it useful!
History
Changed the source object to use a "WeakReference
".