C# How to Find If an Event Is Hooked Up

C# How to find if an event is hooked up

There is a subtle illusion presented by the C# event keyword and that is that an event has an invocation list.

If you declare the event using the C# event keyword, the compiler will generate a private delegate in your class, and manage it for you. Whenever you subscribe to the event, the compiler-generated add method is invoked, which appends the event handler to the delegate's invocation list. There is no explicit invocation list for the event.

Thus, the only way to get at the delegate's invocation list is to preferably:

  • Use reflection to access the compiler-generated delegate OR
  • Create a non-private delegate (perhaps internal) and implement the event's add/remove methods manually (this prevents the compiler from generating the event's default implementation)

Here is an example demonstrating the latter technique.

class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}

Check to see if an event handler is attached to and event

You can implement a class inherited from EventHandler. For this class you can implement any additional behavior you want. For instance, you can create a collection which will hold object-event maps and you can implement a method which searches for a given pair or pattern.

Has an event handler already been added?

From outside the defining class, as @Telos mentions, you can only use EventHandler on the left-hand side of a += or a -=. So, if you have the ability to modify the defining class, you could provide a method to perform the check by checking if the event handler is null - if so, then no event handler has been added. If not, then maybe and you can loop through the values in
Delegate.GetInvocationList. If one is equal to the delegate that you want to add as event handler, then you know it's there.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{
if ( this.EventHandler != null )
{
foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
{
if ( existingHandler == prospectiveHandler )
{
return true;
}
}
}
return false;
}

And this could easily be modified to become "add the handler if it's not there". If you don't have access to the innards of the class that's exposing the event, you may need to explore -= and +=, as suggested by @Lou Franco.

However, you may be better off reexamining the way you're commissioning and decommissioning these objects, to see if you can't find a way to track this information yourself.

How to verify that an event handler has been added

If you just want to verify in the debugger that the event handler was added, you can cast the event to a System.Delegate and look at the invocation list. This can be done for instance in the Visual Studio Immediate Window. Here I am examining the invocation list of an event called "MyEvent", which turns out to have 3 handlers added:

((System.Delegate)MyEvent).GetInvocationList()
{System.Delegate[3]}
[0]: {Method = {Void handler11_MessageReceived(System.Object, MyProject.MyEventArgs)}}
[1]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}
[2]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}

You can also browse the invocation list in the Watch window.

It might even be useful to do this if you are unsure whether some event listeners had been properly disposed of later, for example.

Update: How to check programmatically that one event contains another.

Checking programmatically that an event handler has been added to an event is more complex than one might imagine, because events are actually a form of MulticastDelegate. A multicast delegate can be either an "atomic" delete created by (e.g.) a delegate statement or lambda expression, or a combined delegate created by adding together two or more multicast delegates. And as it turns out, when one combined delegate is added to or subtracted from a second, what actually happens is that its invocation list is added to or subtracted from another. E.g.

        Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("b");
Action c = () => Console.WriteLine("c");
Action d = () => Console.WriteLine("d");

var ab = a + b;
var cd = c + d;
var ad = a + d;
var bc = b + c;

var abcd = ab + cd;
var adbc = ad + bc;
var abc = abcd - d;
var bcd = abcd - a;

bool test1 = (abcd == (a + b + c + d)); // returns true
bool test2 = abcd.GetInvocationList().Contains(a); // returns true;
bool test3 = abcd.GetInvocationList().Contains(ab); // returns **false**;
bool test4 = abc.GetInvocationList().Contains(d); // returns false

If you want to create a public static extension method to check to see whether your handler was added to an event, it should correctly handle the case of a multicast delegate being added. But, does that just mean that the event has all the delegates of the handler? Or do they need to be bound together in sequence, since Delegate.Combine does preserve order? The following extension method checks for the latter:

public static class EventHelper
{
/// <summary>
/// Return true if all the atomic delegates in the multicast delegate handler are bound into the
/// publisher, grouped together and in the same order.
/// </summary>
/// <param name="publisher"></param>
/// <param name="handler"></param>
/// <returns></returns>
public static bool HasBound(this Delegate publisher, Delegate handler)
{
if (publisher == null || handler == null)
return false;
if (publisher == handler)
return true;
var publisherList = publisher.GetInvocationList();
var handlerList = handler.GetInvocationList();
return (publisherList.SublistIndex(handlerList, 0) >= 0);
}

public static bool HasBound<TEventArgs>(this EventHandler<TEventArgs> publisher, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
{
return HasBound((Delegate)publisher, (Delegate)handler);
}

public static bool HasBound(this EventHandler publisher, EventHandler handler)
{
return HasBound((Delegate)publisher, (Delegate)handler);
}
}

public static class ListHelper
{
public static int SublistIndex<T>(this IList<T> list, IList<T> sublist, int start)
{
var comparer = EqualityComparer<T>.Default;
for (int listIndex = start, end = list.Count - sublist.Count + 1; listIndex < end; listIndex++)
{
int count = 0;
while (count < sublist.Count && comparer.Equals(sublist[count], list[listIndex + count]))
count++;
if (count == sublist.Count)
return listIndex;
}
return -1;
}
}

And here are the results of testing:

        bool test08 = a.HasBound(a); // returns true;
bool test09 = b.HasBound(a); // returns true;
bool test10 = abcd.HasBound(a + b + c + d); // returns true;
bool test11 = abcd.HasBound(adbc); // returns false - wrong order.
bool test12 = abcd.HasBound(a); // returns true;
bool test13 = cd.HasBound(a); // return false
bool test14 = bcd.HasBound(bc); // returns true despite the fact that bcd was not created directly from bc.
bool test15 = abcd.HasBound(ad); // returns false because the "ad" events are not adjacent in abcd.

Honestly, I would not do this outside of debugging however. Actually coding a check to see if a listener is bound to an event seems wrong.

Update 2 Is the real question here how to fetch all the event handlers from a Microsoft UIElement? It has to be done through some nasty reflection that isn't guaranteed to work in future .Net versions, as is shown here: How to remove all Click event handlers? and here: How would it be possible to remove all event handlers of the 'Click' event of a 'Button'?.

How to know if event was attached in run time

There is no way to determine if an event was ever attached or removed, particularly not from an event in a class you didn't define. An event defined in another class can only appear on the left side of an add/remove operation - attempting to do otherwise will result in the compiler telling you as much as an error.

If you want to know about this for some reason that can't be better suited otherwise, consider instead raising a flag when attaching this even handler, so that you can query said flag later.

Check if a specific event handler method already attached

No. You cannot.

The event keyword was explicitly invented to prevent you from doing what you want to do. It makes the delegate object for the event inaccessible so nobody can mess with the events handlers.

Source : How to dermine if an event is already subscribed

How to check which event are assigned?

Windows Forms (WinForms) has a tricky model of events for components (and DataGridView is a component). Some events are inherited from Control (like FontChanged, ForeColorChanged, etc.), but all specific to component events are stored in a single EventHandlerList object, which is inherited from Component (BTW, events from Control are also stored there, see the update at the end of the answer). There is a protected Events property for that:

protected EventHandlerList Events
{
get
{
if (this.events == null)
this.events = new EventHandlerList(this);
return this.events;
}
}

And here is the way how event handlers are added for DataGridView events:

public event DataGridViewCellEventHandler CellValueChanged
{
add { Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
remove { Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
}

As you can see, delegate (value) is passed to EventHandlerList with some key value. All event handlers are stored there by key. You can think about EventHandlerList as a dictionary with objects as keys, and delegates as values. So, here is how you can get components' events with reflection. The first step is getting those keys. As you already noticed, they are named as EVENT_XXX:

private static readonly object EVENT_DATAGRIDVIEWCELLVALUECHANGED;
private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEUP;
// etc.

So here we go:

var keys = typeof(DataGridView) // You can use `GetType()` of component object.
.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(f => f.Name.StartsWith("EVENT_"));

Next, we need our EventHandlerList:

var events = typeof(DataGridView) // or GetType()
.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
// Could be null, check that
EventHandlerList handlers = events.GetValue(grid) as EventHandlerList;

And the last step, getting the list of keys, which have handlers attached:

var result = keys.Where(f => handlers[f.GetValue(null)] != null)
.ToList();

That will give you the keys. If you need delegates, then simply look in the handlers list for them.

UPDATE: Events which inherited from Control are also stored in EventHandlerList, but for some unknown reason their keys have different names, like EventForeColor. You can use the same approach as above to get those keys and check if handlers are attached.

How to count how many listeners are hooked to an event?

You can use GetInvocationList():

MessageReceived?.GetInvocationList().Length


Related Topics



Leave a reply



Submit