C# How to find if an event is hooked up

Viewed 60834

I want to be able to find out if an event is hooked up or not. I've looked around, but I've only found solutions that involved modifying the internals of the object that contains the event. I don't want to do this.

Here is some test code that I thought would work:

// Create a new event handler that takes in the function I want to execute when the event fires
EventHandler myEventHandler = new EventHandler(myObject_SomeEvent);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length;
// Now actually hook an event up
myObject.SomeEvent += m_myEventHandler;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length;

Unfort the above is dead wrong. I thought that somehow the "invocationList" in myEventHandler would automatically get updated when I hooked an event to it. But no, this is not the case. The length of this always comes back as one.

Is there anyway to determine this from outside the object that contains the event?

5 Answers

If the object concerned has specified the event keyword, then the only things you can do are add (+=) and remove (-=) handlers, nothing more.

I believe that comparing the invocation list length would work, but you need to be operating inside the object to get at it.

Also, keep in mind that the += and -= operators return a new event object; they don't modify an existing one.

Why do you want to know if a particular event is hooked up? Is it to avoid registering multiple times?

If so, the trick is to remove the handler first (-=) as removing a handler that's not there is legal, and does nothing. Eg:

// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;

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; }
    }
}

You should be able to get the invocation list via the "event". Roughly, it will be something like..

public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
   var d = this._MyEvent.GetInvocationList(); //Delegate[]
   return d.Length;
}

I used your example and modified it a little bit. registering an event handler increases the number of invocations. even when using two different callback methods (as shown here) or using the same callback method.

private void SomeMethod()
{
    // Create a new event handler that takes in the function I want to execute when the event fires
    var myEventHandler = new EventHandler(OnPropertyChanged);

    // Get "p1" number events that got hooked up to myEventHandler
    int p1 = myEventHandler.GetInvocationList().Length; // 1

    // Now actually hook an event up
    myEventHandler += OnPropertyChanged2;

    // Re check "p2" number of events hooked up to myEventHandler
    int p2 = myEventHandler.GetInvocationList().Length; // 2

    myEventHandler.Invoke(null, null); 
// each of the registered callback methods are executed once. 
// or if the same callback is used, then twice.
}

private void OnPropertyChanged2(object? sender, EventArgs e)
{}
private void OnPropertyChanged(object? sender, EventArgs e)
{}

As others already mentioned, the access to eventhandler.GetInvocationList is limited to the class itself, you need to expose a property or method to retrieve the delegate list.

Like this:

protected Delegate[]? GetInvocations() => PropertyChanged?.GetInvocationList();

depending on your usage make it protected, internal or both.

Related