Click here to Skip to main content
15,867,594 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.6K   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 
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.