Click here to Skip to main content
15,884,783 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.7K   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 
Hi supercat,

I don't get your concern for thread safety here. I don't see the difference between calling the += operator and verses using reflection as far as thread safety is concerned. Both execute in your thread. Non-static instances of MulticastDelegate are not thread safe anyway so what's the difference?

The concept of adding or removing handlers of a foreign object, you do this no matter what call you do. You either do it through reflection or the delegate itself. It is always going to run in your thread and any locking of the object contained by the source object must implement thread safety if there is to be any, and Microsoft did not do it for the MulticastDelegate.

My call goes through reflection, not me calling a dubious function to backdoor the entry in the object's invocation list. In addition, it is quite possible that the function uses the operator, the operator uses the function, or that they both go through a common implementation. That is the responsibility of the implementer of the component if he wishes to ensure type safety. Calling through reflection is very common practice so you should have this same concern for every object that manipulates another object through reflection.

I do agree with you on the WeakReference usage of the source object. As you can see in the history comments it has been added to the latest version.

Thanks for that WeakReference suggestion. At the risk of sounding arrogant, I don't see the issue of thread safety as far as my implementation is concerned as concerning thread safety of the delegate, "it doesn't bring anything to the party, and it doesn't take anything away".

Jim
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.