Working with Event Handlers in .NET
When adding handler开发者_StackOverflow中文版s 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?
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.
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.
精彩评论