Although the .Net reflection APIs make some tasks easier, we’ve found recently that those related to events and their delegates have been fairly poorly designed. Our task was very simple: dynamically manipulate the event handlers attached to a set of events.
It’s easy enough to get a declaration about an event of some sort using code like:
EventInfo infoAboutEvent = someObject.GetType().GetEvent("MyEvent");
Given your EventInfo
class it’s easy to add an a new event handler. Unfortunately going the other way around ended up much harder. Unlike things like FieldInfo
that give you direct access to get the field instance from the object using GetValue(owner)
, finding the event field isn’t as easy. First we had to look at all the fields, and filtered out things that looked like a Delegate so we could do some useful things with them, like finding out what subscribers were hooked up to our event.
Our ultimate goal of being able to dynamically remove EventHandler
s at runtime further ran into problems when we were trying to unhook methods from private events. The result of trying to unhook a handler from a private event via the EventInfo.RemoveEventHandler
method results in an InvalidOperationException
with a message of “Cannot remove the event handler since no public remove method exists for the event.”
One more simple use of reflection and soon we were able to automatically unhook a variety of delegates from our events regardless of their access mechanism (although using code that wasn’t as nice as we wanted). A few refactorings later and we ended up with something actually quite useful in the end.
ReflectUtil.RemoveEventHandlersFrom( delegate(Delegate subject) { return subject.Method.Name.StartsWith("On"); }, objectWithEvent);
And here’s the class that we ended up with. Although I can’t recommend people use reflection lightly, if you need to delve into the world of reflection with events, I hope you find this example useful.
using System; using System.Collections.Generic; using System.Globalization; using System.Reflection; public sealed class ReflectUtil { private static BindingFlags PrivatePublicStaticInstance = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; public delegate bool MatchesOnDelegate(Delegate subject); public static void RemoveEventHandlersFrom( MatchesOnDelegate matchesOnDelegate, params object[] objectsWithEvents) { foreach (object owningObject in objectsWithEvents) { foreach (DelegateInfo eventFromOwningObject in GetDelegates(owningObject)) { foreach (Delegate subscriber in eventFromOwningObject.GetInvocationList()) { if (matchesOnDelegate(subscriber)) { EventInfo theEvent = eventFromOwningObject.GetEventInfo(); RemoveSubscriberEvenIfItsPrivate(theEvent, owningObject, subscriber); } } } } } // You can use eventInfo.RemoveEventHandler(owningObject, subscriber) // unless it's a private delegate private static void RemoveSubscriberEvenIfItsPrivate( EventInfo eventInfo, object owningObject, Delegate subscriber) { MethodInfo privateRemoveMethod = eventInfo.GetRemoveMethod(true); privateRemoveMethod.Invoke(owningObject, PrivatePublicStaticInstance, null, new object[] {subscriber}, CultureInfo.CurrentCulture); } private static DelegateInfo[] GetDelegates(object owningObject) { List delegates = new List(); FieldInfo[] allPotentialEvents = owningObject.GetType() .GetFields(PrivatePublicStaticInstance); foreach (FieldInfo privateFieldInfo in allPotentialEvents) { Delegate eventFromOwningObject = privateFieldInfo.GetValue(owningObject) as Delegate; if (eventFromOwningObject != null) { delegates.Add(new DelegateInfo(eventFromOwningObject, privateFieldInfo, owningObject)); } } return delegates.ToArray(); } private class DelegateInfo { private readonly Delegate delegateInformation; private readonly FieldInfo fieldInfo; private readonly object owningObject; public DelegateInfo(Delegate delegateInformation, FieldInfo fieldInfo, object owningObject) { this.delegateInformation = delegateInformation; this.fieldInfo = fieldInfo; this.owningObject = owningObject; } public Delegate[] GetInvocationList() { return delegateInformation.GetInvocationList(); } public EventInfo GetEventInfo() { return owningObject.GetType().GetEvent(fieldInfo.Name, PrivatePublicStaticInstance); } } }