Click here to Skip to main content
15,891,905 members
Articles / Programming Languages / C#

EventSubscriptionManager - No More Leaking Event Delegates

Rate me:
Please Sign up or sign in to vote.
3.67/5 (4 votes)
8 Mar 2010CPOL4 min read 32.8K   148   18   18
A solution that manages attached events

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.

  1. It has to be easy to use. (otherwise, what's the point right?)
  2. It had to allow me to subscribe and unsubscribe at will.
  3. It had to maintain multiple subscriptions for any given event.
  4. It has to support all event delegates even those not inheriting from EventHandler.
  5. 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:

C#
 // subscribing to get a method in your class, called MyEventMethod
 someObject.SomeEvent += new SomeEventHandler(MyEventMethod); 

// or the shorthand way: someObject.SomeEvent += new MyEventMethod;

Likewise...the unsubscription is done similarly:

C#
// time to unsubscribe to events....
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:

C#
using EventSubscription.Manager;  

Third, add an instance member of the class to the Form or Class you want to manage resources:

C#
EventSubscriptionManager  m_eventMgr = new EventSubscriptionManager(); 

Now you are ready. Using the example above, we will subscribe to the same event:

C#
// subscribing to get a function called MyEventMethod
m_eventMgr.Subscribe(someObject, "SomeEvent", new SomeEventHandler(MyEventMethod)); 

Likewise...the unsubscription is done similarly:

C#
// time to unsubscribe to events....
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:

C#
m_eventMgr.UnsubscribeAll(); // reset our subscriptions...

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.

C#
EventInfo eventInfo = eventSource.GetType().GetEvent(m_eventName, c_flags);  

From this class, we can add our event handler to the sources event via:

C#
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:

C#
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".

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
benmor12-Oct-10 6:51
benmor12-Oct-10 6:51 
GeneralIssues [modified] Pin
benmor10-Oct-10 6:55
benmor10-Oct-10 6:55 
GeneralRe: Issues Pin
rittjc10-Oct-10 7:47
rittjc10-Oct-10 7:47 
GeneralRe: Issues [modified] Pin
benmor10-Oct-10 9:35
benmor10-Oct-10 9:35 
GeneralRe: Issues Pin
rittjc10-Oct-10 10:03
rittjc10-Oct-10 10:03 
GeneralRe: Issues Pin
benmor10-Oct-10 10:34
benmor10-Oct-10 10:34 
GeneralRe: Issues Pin
rittjc10-Oct-10 13:31
rittjc10-Oct-10 13:31 
Hi Ben,

Ok, then we will go that route, using your scenario above, certainly "form" will be collected. Why would it not? someB DOES NOT hold reference to "form" at all. Neither does _mgr. When you return from ShowFormA() "form" will be destroyed as it goes out of scope and there are no more references to it. During the destruction of "form" its _mgr holds a strong reference to a WeakReference object which holds the weak reference to the someB object that it was subscribed with. With "form" being destroyed, it of course destroys _mgr, which unsubscribes the WeakReference to someB (assuming B still exists) and then releases its strong reference to the WeakReference object (m_eventSource) it owns. I guess what must be confusing you is that there are not references to "form" other than the one in the local one in the ShowFormA method. Once a local object goes out of scope, its reference is removed and it then gets scheduled for collection. Am I missing something in what you are saying here?

But even if you mean something else, there is something you should consider here. someB in your scenario holds a strong reference to the delegate (m_eventSource) owned by _mgr. BUT, m_eventSource holds a weak reference to the someB event target. Now this delegate cannot be released if B remains instantiated or is not unsubscribed. But _mgr can be destroyed, and someB can be destroyed. Now, lets say someB is destroyed with the event handler being unsubscribed to. Nobody cares. There are no strong references to it. "Jimmy cracked corn and I don't care..." Smile | :) The GC gets rid of someB's delegate and it is not leaked.

Lets say "form" is destroyed first and it does not unregister its events explicitly through _mgr. Still no problem. When it is destroyed, it releases its reference to _mgr. That is the only reference to _mgr so it will be destroyed. In its destruction, it unsubscribes to someB delegate(and any other event target delegate it has subscribed). This releases someB's reference to the WeakReference delegate (m_eventSource) in _mgr. Then it releases its reference to the m_eventSource WeakReference (which was just unregistered with someB) and it gets collected. Then _mgr and form get collected. The only thing not collected is someB. But it is not "form"'s responsibility nor any of its contained objects.

Here are the references in the picture:

"form" -S-> _mgr -S-> m_eventSource -W-> someB -S-> m_eventSource

Notice the bidirectional references of someB (or better said its Multicast delegate). It is strong in only one direction. It it was strong in both directions then someB's event delegate could not be released. So when someB is destroyed, it would release its reference to the Multicast delegate but m_eventSource would not and it would be "leaked". But, since m_eventSource only holds a weak reference to someB's event delegate, then it does not need to release anything. The GC does not know about weak delegates. That's the point of using them.


Clear now?


Jim
GeneralRe: Issues Pin
benmor10-Oct-10 19:16
benmor10-Oct-10 19:16 
GeneralRe: Issues Pin
rittjc10-Oct-10 19:43
rittjc10-Oct-10 19:43 
GeneralRe: Issues Pin
benmor10-Oct-10 20:50
benmor10-Oct-10 20:50 
GeneralUpdated to use a Weak Reference Pin
rittjc8-Mar-10 15:27
rittjc8-Mar-10 15:27 
QuestionThread safety? Pin
supercat98-Mar-10 9:39
supercat98-Mar-10 9:39 
AnswerRe: Thread safety? Pin
rittjc8-Mar-10 15:42
rittjc8-Mar-10 15:42 
GeneralRe: Thread safety? Pin
supercat99-Mar-10 5:15
supercat99-Mar-10 5:15 
GeneralWeakEventManager Pin
Steve Hansen8-Mar-10 4:44
Steve Hansen8-Mar-10 4:44 
GeneralRe: WeakEventManager Pin
supercat98-Mar-10 9:32
supercat98-Mar-10 9:32 
GeneralRe: WeakEventManager Pin
Steve Hansen8-Mar-10 11:13
Steve Hansen8-Mar-10 11:13 
GeneralRe: WeakEventManager Pin
rittjc8-Mar-10 14:12
rittjc8-Mar-10 14:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.