3

When adding handlers indiscriminately to an object's events, I realized that I can attach the same handler to an event as many times as I like. This means the handler is called once for each time it was attached.

I'm wondering these things:

  • Is there a way to look at which handlers have been added to an object's event?
  • Is it possible to remove all handlers from an event?
  • Where are these correlations between an event and its handler stored?
Daniel
  • 10,864
  • 22
  • 84
  • 115
  • 1
    Clear handlers: http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control – Dave Swersky Feb 17 '11 at 19:06
  • This is a *very* strong violation of good coding principles. The one reason that the *event* keyword exists in the first place, it puts a very effective stop to it. – Hans Passant Feb 17 '11 at 19:26
  • @Hans Passant: I would think it a good idea for an object which is disposed to destroy its event subscription list. Otherwise, if a live reference to the disposed object exists, all of the object's former subscribers will be kept alive as well. Is there any reason a disposed object which won't be raising any more events shouldn't kill its subscription list? – supercat Feb 22 '11 at 18:02
  • An object has no trouble managing *it's own* event subscriber list. You just don't get to mess with another object's list. – Hans Passant Feb 22 '11 at 18:07

2 Answers2

2

If the event is marked with the C# event keyword then there's no way from outside the object to see the subscribers - the requisite information is just not visible.

From inside, it can be done, though it is complex and relies on details of implementation that might change (though, they haven't yet).

A workaround that might be useful for you though, is that it's valid to remove a handler that's not there - no exception is thrown.

So this code is valid:

myConnection.Closing -= ConnectionClosingHandler;
myConnection.Closing += ConnectionClosingHandler;

If you're already subscribed to the event, the first line removes the subscription.
If you're not already subscribed to the event, the first line does nothing.

The second line then hooks up a new subscription, and you're guaranteed not to be notified multiple times.

To answer your last bullet point, when you declare a normal event:

public event PropertyChangedEventHandler Changed;

The compiler creates a member variable of type PropertyChangedEventHandler which stores all the subscribers. You can take over storage if you want:

public event PropertyChangedEventHandler Changed
{ 
    add { ... }
    remove { ... }
}

The use of -= and += to modify the subscription isn't syntactic sugar - the delegates are immutable, and a new instance is returned when you add or remove a handler. Have a look at Delegate and MulticastDelegate (both MSDN links) for more information on how this works.

Bevan
  • 43,618
  • 10
  • 81
  • 133
1

The correlations between an event and its handlers are stored on the event itself. When you access the event, this information is actually copied out into a method group. That's why you're supposed to say:

var onclick = Click;
if (onclick != null) onclick();

If I accessed the Click event twice rather than using the intermediate onclick variable, I would have caused any events to be copied twice. Also, in a multi-threaded scenario, if someone removed a handler between checking Click != null and invoking the handler, I could end up throwing an exception.

If you already know which handler you want to remove, it is easy to remove that handler:

EventHandler handler1 = (sender, e) => Console.WriteLine("test");
Click += handler1;
Click -= handler1;

There is a way to get some basic information about each handler that was added to an object's event, via GetInvocationList:

foreach(var handler in Click.GetInvocationList())
    Console.WriteLine(handler.Method.ToString());

However, the information you get out is in the form of a Delegate object. It can be invoked (which can be useful if you want to catch any exceptions thrown by one handler, and continue invoking the remaining handlers), but C# doesn't provide an easy way to remove the handler from the event based solely on this information. Some of the answers at How to remove all event handlers from a control seem to indicate that you can use Reflection to do it, or you can use Visual Basic's RemoveHandler command.

Community
  • 1
  • 1
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315