Click here to Skip to main content
16,016,678 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 133.3K   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

 
BugFound bug in BuildEventFields Pin
Code Blocks 9511-Dec-18 8:36
Code Blocks 9511-Dec-18 8:36 
GeneralRe: Found bug in BuildEventFields Pin
Colin Lamarre27-Feb-20 6:34
Colin Lamarre27-Feb-20 6:34 
QuestionHoping for a better out come Pin
TexG18-Oct-17 6:44
TexG18-Oct-17 6:44 
QuestionProblem when inherit an own Control from other Control Pin
baer99929-Dec-14 2:51
baer99929-Dec-14 2:51 
GeneralGood stuff! Pin
westville_mike29-Aug-13 5:37
westville_mike29-Aug-13 5:37 
GeneralMy vote of 5 Pin
Marius Samoila23-Jan-13 12:49
Marius Samoila23-Jan-13 12:49 
NewsMessage from the Author Pin
Hedley Muscroft17-Jan-13 21:58
Hedley Muscroft17-Jan-13 21:58 
QuestionHere is a Version wich supports WPF Pin
jogibear998812-Dec-12 11:56
jogibear998812-Dec-12 11:56 
GeneralMy vote of 4 Pin
skyslogd19-Sep-12 16:47
skyslogd19-Sep-12 16:47 
QuestionDoes not Work in Silverlight Pin
vk00024-Apr-12 11:34
vk00024-Apr-12 11:34 
GeneralMy vote of 5 Pin
Ilka Guigova3-Nov-11 7:41
Ilka Guigova3-Nov-11 7:41 
QuestionDoesn't seem to work with ComboBox events Pin
joebarthib26-Aug-11 4:44
joebarthib26-Aug-11 4:44 
AnswerRe: Doesn't seem to work with ComboBox events Pin
skyslogd19-Sep-12 16:01
skyslogd19-Sep-12 16:01 
QuestionIn VB.Net this is not working properly Pin
GrowingTech31-May-11 5:34
GrowingTech31-May-11 5:34 
AnswerRe: In VB.Net this is not working properly Pin
Hedley Muscroft12-Jul-11 5:03
Hedley Muscroft12-Jul-11 5:03 
GeneralRe: In VB.Net this is not working properly Pin
GrowingTech13-Jul-11 17:56
GrowingTech13-Jul-11 17:56 
GeneralRe: In c#.Net this is not working properly Pin
lawanya31-Jul-11 23:08
lawanya31-Jul-11 23:08 
AnswerRe: In c#.Net this is not working properly Pin
stumpyfr29-Nov-13 5:17
stumpyfr29-Nov-13 5:17 
General[My vote of 2] Doesn't work in silverlight Pin
JaiderNet9-May-11 9:57
professionalJaiderNet9-May-11 9:57 
GeneralRe: [My vote of 2] Doesn't work in silverlight Pin
Hedley Muscroft12-Jul-11 5:00
Hedley Muscroft12-Jul-11 5:00 
QuestionWhere's the code? Pin
PIEBALDconsult21-Dec-10 8:53
mvePIEBALDconsult21-Dec-10 8:53 
GeneralAmbigiousMatchException Pin
drake8-Dec-10 1:15
drake8-Dec-10 1:15 
GeneralMy vote of 5 Pin
AhsanS21-Sep-10 6:50
AhsanS21-Sep-10 6:50 
GeneralMy vote of 5 Pin
Bigdeak12-Sep-10 22:36
Bigdeak12-Sep-10 22:36 
GeneralGreat stuff ... Pin
Sushant Joshi9-Sep-10 4:09
Sushant Joshi9-Sep-10 4:09 

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.