Click here to Skip to main content
15,878,809 members
Articles / Programming Languages / C#

Removing Event Handlers using Reflection

Rate me:
Please Sign up or sign in to vote.
4.95/5 (47 votes)
23 Aug 2010CPOL4 min read 128.4K   6.5K   62   34
How to remove an object's Event Handlers using Reflection

Introduction

I've recently been working on a pretty massive WinForms project which is very complex. We noticed that the application was 'leaking' memory (i.e. the memory usage was continually growing despite objects being correctly 'Disposed').

Having run the application through SciTech's excellent .NET Memory Profiler, we discovered the cause: "Direct EventHandler Roots".

One of the biggest causes of "memory leaks" in a .NET application is when an unintended reference keeps a Managed object alive (the other being Managed objects which hold Unmanaged resources).

Simple example:

C#
A.SomeEvent += new EventHandler(B.SomeMethod);

Later, you finish with B (but not with A) and Dispose of it. However because A is holding a reference to B (via an EventHandler), B will never get garbage collected while A is still alive.

Big deal? Can't you just do:

C#
A.SomeEvent -= new EventHandler(B.SomeMethod); 

Absolutely! And that will de-couple the objects and allow them to be GC'd correctly.

HOWEVER, in the real world (and especially in big complex applications which have grown 'organically' over time) you can find yourself dealing with dozens of different object types and scores of object instances where the interplay between the classes via Delegates and EventHandlers can be nightmarish to work through.

Furthermore, when EventHandlers / Delegates are assigned dynamically at runtime, it may be impossible to know at any one time what Delegates are assigned to what EventHandlers, thus making it impossible to use -= to remove the appropriate delegate. And what about removing anonymous delegates? It all gets very hairy.

Of course, if you know from the outset that you're going to use this sort of "EventHandler-heavy" model, then you should really use Weak Referenced EventHandlers. (Please see John Stewien's excellent article about this for more information.)

Anyway, if you find yourself in a fix with an application which is leaking memory due to rooted EventHandler references, and you don't have time to refactor the code or add -= counterparts to all the +='s, then this may help!

Using the Code

Attached is a simple static class (cEventHelper) for you to incorporate into your own code.

If you're using SciTech's .NET Memory Profiler (or something similar) and you uncover an object which is holding on to a reference via its EventHandlers, then simply call:

C#
cEventHandler.RemoveAllEventHandlers(naughty_object);

Alternatively, if you know exactly which event is causing the problem and you don't want to unhook all of the events, then you can call:

C#
cEventHelper.RemoveEventHandler(naughty_object, "SomeEvent");

Simple as that.

How It Works

When you first call cEvent.RemoveAllEventHandlers (or cEvent.RemoveEventHandler), the class builds a list of System.Reflection.FieldInfo objects for each of the Events belonging to that type.

Why FieldInfo objects and not EventInfo objects? Because the underlying FieldInfo object is required to get at the invocation list for the Event that it represents, hence that's the object that we're interested in.

Once all of the FieldInfo objects have been collected for a given type, they are cached in a Dictionary for performance reasons (you could easily edit the code to remove the caching if you wish). Now it's just a matter of using Reflection to get the invocation list and removing all the assigned delegates.

This was (reasonably) easy to do for INSTANCE events, but you have to handle STATIC events differently. To be honest, it took me ages to work out how to do this and it basically involved tonnes of trial-and-error with various code snippets scattered around the internet. We got there in the end though and cEventHelper will happily remove both INSTANCE and STATIC EventHandlers.

For more information on what's going on inside the class, the main routine to look at is cEventHelper.RemoveEventHander(...).

Points of Interest

I learned something new: Type.GetEvent (and Type.GetEvents) will get all EventInfos for the type AND its ancestors, while Type.GetField (and Type.GetFields) get only the FieldInfos for the exact type.

The BindingFlags.FlattenHierarchy doesn't help because that only works on PROTECTED & PUBLIC members and the FieldInfos we're getting are PRIVATE. That's why in the BuildEventFields(...) routine, you'll see that after getting all the EventInfos, we have to get the 'DeclaringType' before we can use Type.GetField.

Closing Comments

Once again, I'd like to stress that I wouldn't recommend this approach if you have the luxury of starting a new project or if your project is small enough to refactor the code to use Weak Reference EventHandlers. However, if you're in a fix (like I was), this could certainly help out!

History

  • 1.0 Initial release
  • 1.1 Updated code (and article) to use MemberInfo.DeclaringType (thanks to Steve Hansen for pointing this out!)

License

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


Written By
Software Developer (Senior) Pioneer Software Ltd
United Kingdom United Kingdom
Hedley is a Director of Pioneer Software Ltd, a small UK-based software company specialising in Windows application development.
He started programming back in the mid-80's in C++ and later specialised in Borland's C++ Builder and Delphi.
After several years as a contractor, he founded Pioneer Software Ltd in the mid-90's to concentrate on developing bespoke Windows applications.
Current 'language/environment of choice' is C# and VS2010.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Tiefeng You30-Aug-10 5:05
Tiefeng You30-Aug-10 5:05 
GeneralI like it, you mention WeakEvents also, you may also like these links in my MVVM framework Pin
Sacha Barber23-Aug-10 22:13
Sacha Barber23-Aug-10 22:13 
GeneralMy Vote of 5 Pin
ScruffyDuck23-Aug-10 19:57
ScruffyDuck23-Aug-10 19:57 
GeneralDeclaringType Pin
Steve Hansen23-Aug-10 1:44
Steve Hansen23-Aug-10 1:44 
GeneralRe: DeclaringType Pin
Hedley Muscroft23-Aug-10 7:57
Hedley Muscroft23-Aug-10 7:57 
GeneralMy vote of 5 Pin
Pete O'Hanlon22-Aug-10 23:04
mvePete O'Hanlon22-Aug-10 23:04 
GeneralTake five Pin
Syed Saqib Ali Tipu21-Aug-10 18:54
Syed Saqib Ali Tipu21-Aug-10 18:54 
JokeRe: Take five Pin
AspDotNetDev21-Aug-10 20:41
protectorAspDotNetDev21-Aug-10 20:41 
GeneralMy vote of 5 Pin
#realJSOP21-Aug-10 0:35
mve#realJSOP21-Aug-10 0:35 

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.