Click here to Skip to main content
Click here to Skip to main content

Tagged as

Removing Event Handlers using Reflection

, 23 Aug 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

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:

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:

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:

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)

Share

About the Author

Hedley Muscroft
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

 
GeneralGood stuff! Pinmemberwestville_mike29-Aug-13 5:37 
GeneralMy vote of 5 PinmemberMarius Samoila23-Jan-13 12:49 
NewsMessage from the Author PinmemberHedley Muscroft17-Jan-13 21:58 
QuestionHere is a Version wich supports WPF Pinmemberjogibear998812-Dec-12 11:56 
GeneralMy vote of 4 Pinmemberblackout_19-Sep-12 16:47 
QuestionDoes not Work in Silverlight Pinmembervk00024-Apr-12 11:34 
GeneralMy vote of 5 PinmemberIlka Guigova3-Nov-11 7:41 
QuestionDoesn't seem to work with ComboBox events Pinmemberjoebarthib26-Aug-11 4:44 
AnswerRe: Doesn't seem to work with ComboBox events [modified] Pinmemberblackout_19-Sep-12 16:01 
QuestionIn VB.Net this is not working properly PinmembergloierTech31-May-11 5:34 
AnswerRe: In VB.Net this is not working properly PinmemberHedley Muscroft12-Jul-11 5:03 
GeneralRe: In VB.Net this is not working properly PinmembergloierTech13-Jul-11 17:56 
GeneralRe: In c#.Net this is not working properly Pinmemberlawanya31-Jul-11 23:08 
AnswerRe: In c#.Net this is not working properly Pinmemberstumpyfr29-Nov-13 5:17 
General[My vote of 2] Doesn't work in silverlight PinmemberMember 41591749-May-11 9:57 
GeneralRe: [My vote of 2] Doesn't work in silverlight PinmemberHedley Muscroft12-Jul-11 5:00 
QuestionWhere's the code? PinmvpPIEBALDconsult21-Dec-10 8:53 
GeneralAmbigiousMatchException Pinmemberdrake8-Dec-10 1:15 
GeneralMy vote of 5 PinmemberAhsanS21-Sep-10 6:50 
GeneralMy vote of 5 PinmemberBigdeak12-Sep-10 22:36 
GeneralGreat stuff ... PinmemberSushant Joshi9-Sep-10 4:09 
GeneralMy vote of 5 PinmemberTiefeng You30-Aug-10 5:05 
GeneralI like it, you mention WeakEvents also, you may also like these links in my MVVM framework PinmvpSacha Barber23-Aug-10 22:13 
GeneralMy Vote of 5 PinmemberScruffyDuck23-Aug-10 19:57 
GeneralDeclaringType PinmemberSteve Hansen23-Aug-10 1:44 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.2 | Last Updated 23 Aug 2010
Article Copyright 2010 by Hedley Muscroft
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid