Daniel
Daniel

Reputation: 11054

Working with Event Handlers in .NET

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:

Upvotes: 3

Views: 452

Answers (2)

StriplingWarrior
StriplingWarrior

Reputation: 156469

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.

Upvotes: 1

Bevan
Bevan

Reputation: 44307

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.

Upvotes: 2

Related Questions