Have you ever wanted to remove a handler from an event, but haven't had a reference to the object receiving the event, or perhaps you don't even know what type the object has! .NET hides the details of events but with a bit of help from the Reflection classes, you can edit the handlers for your own purposes.
Cautionary note
In general, this is a really, really, really bad thing to do! I'm writing about it partly because it is educational, and partly because it was an interesting problem to solve.
Behind the scenes of .NET events
I'm going to be using C# throughout this post, but the comments apply equally to those of you using Visual Basic .NET.
You declare an event in C# using the event keyword. In its simplest form it looks like the following:
public class EventClass
{
public event EventHandler MyEvent;
}
The compiler converts this into four class members:
- A private, instance field named MyEvent of type EventHandler (which is derived from System.MulticastDelegate)
- A public, instance event named MyEvent of type EventHandler (which wraps the private field)
- A public, instance method named add_MyEvent (to attach an event handler)
- A public, instance method named remove_MyEvent (to remove an attached event handler)
To manipulate the handler list you need access to the private field. This cannot be accomplished using C# code directly. Instead, you need to use reflection to access the field.
Getting access to the private field
To get access to the private field, you need to use the reflection classes. In particular you need to specify that you want to bind to a non-public, instance field. This is achieved as follows:
// Add a using statement for System.Reflection
// Assumes an instance of EventClass called e
Type t = e.GetType();
FieldInfo f = t.GetField("MyEvent",
BindingFlags.Instance |
BindingFlags.NonPublic);
The name of the field will match the name of the event, in this case it is MyEvent.
Manipulating the handler list
The value of the field is either null or an instance of a MulticastDelegate. A MulticastDelegate wraps a list of System.Delegate instances. The list is ordered and so the order should also be preserved within any manipulation. MulticastDelegates are also immutable so we need to create a new MulticastDelegate with only those handlers we want to keep. The process is as follows:
- Retrieve the current value of the private field
- Check whether any handlers are currently attached
- Get the list of Delegates currently attached
- Create a new, filtered list of Delegates
- Create a new MulticastDelegate using the filtered list of Delegates
- Set the value of the field to the newly created MulticastDelegate
Step 1: Retrieve the current value of the private field
Retrieving the existing value is straightforward:
object originalValue = f.GetValue(e);
Step 2: Check whether any handlers are currently attached
The value of the field will be null if no handlers are attached, so wrap the remaining steps in a conditional block:
if (originalValue != null) {
...
}
Step 3: Get the list of Delegates currently attached
The value must be cast to the type System.MulticastDelegate. Then, the instance method GetInvocationList of MulticastDelegate can be used to get the list of attached handlers:
System.MulticastDelegate originalDelegate =
(System.MulticastDelegate) originalValue;
System.Delegate[] originalHandlers =
originalDelegate.GetInvocationList();
Step 4: Create a new, filtered list of Delegates
The following code illustrates how to remove all handlers registered by a class DelegateHacking.A. It has not been written particularly efficiently, but it demonstrates the process:
// requires: using System.Collections.Generic;
List<System.Delegate> newHandlers =
new List<System.Delegate>();
foreach (System.Delegate handler in originalHandlers)
{
// compare the type of the class containing
// the event handler with a known type
// to be filtered
if (handler.Method.ReflectedType.Equals(
typeof(DelegateHacking.A)))
{
// don't add any matches to the list
continue;
}
// add the handler to the filtered list
newHandlers.Add(handler);
}
Step 5: Create a new MulticastDelegate using the filtered list of Delegates
You cannot create a Delegate instance directly. Instead, you need to use the static methods of Delegate or MulticastDelegate. In this case, the Combine method of the MulticastDelegate class is used:
System.MulticastDelegate newValue =
System.MulticastDelegate.Combine(
newHandlers.ToArray());
Step 6: Set the value of the field to the newly created MulticastDelegate
This final step sets the value of the field to the new delegate. It is interesting to note that if the list of event handlers had been filtered completely, so that none were left, the value returned by MulticastDelegate.Combine would be null.
The manipulation in full
using System;
using System.Reflection;
using System.Collections.Generic;
public class EventClass
{
public event EventHandler MyEvent;
}
public class Test
{
public static void ManipulateHandlers(EventClass e)
{
Type t = e.GetType();
FieldInfo f = t.GetField("MyEvent",
BindingFlags.Instance |
BindingFlags.NonPublic);
object originalValue = f.GetValue(e);
if (originalValue != null) {
System.MulticastDelegate originalDelegate =
(System.MulticastDelegate) originalValue;
System.Delegate[] originalHandlers =
originalDelegate.GetInvocationList();
List<System.Delegate> newHandlers =
new List<System.Delegate>();
foreach (System.Delegate handler
in originalHandlers)
{
// compare the type of the class containing
// the event handler with a known type
// to be filtered
if (handler.Method.ReflectedType.Equals(
typeof(DelegateHacking.A)))
{
// don't add any matches to the list
continue;
}
// add the handler to the filtered list
newHandlers.Add(handler);
}
System.MulticastDelegate newValue =
System.MulticastDelegate.Combine(
newHandlers.ToArray());
f.SetValue(e, newValue);
}
}
}
Full example source code
You can download the code for a full console application example here (you'll need to rename it):
Acknowledgements
Versions
- Microsoft .NET Framework 2.0
Metadata
- Categories: Software Development, .NET, Reflection, Events, Delegates
- Additional keywords: event handlers, System.Delegate, System.MulticastDelegate
- Technorati Tags: .NET, software development, CSharp, C#, reflection, delegates, events, .NET internals