Intercepting Add and Remove of C# Event Delegates
| Save/Share |
| |||||
![]() |
All you geniuses and rocket scientists out there probably already know this, but I just stumbled on it, and I think it’s kind of cool. Did you know you can customize the adding and removing of C# event handlers? I didn’t.
I’m going to end this post with the question “Yes, but what’s it good for?” You folks with the Ph. D. in C#, feel free to scroll to the bottom and post your answers. For my fellow mortals out there, let me recap what I’m talking about.
We all know how to declare events on a class, right? It goes something like this:
using System;
namespace AddRemoveDelegates
{
public class Account
{
private decimal balance;
public event EventHandler<EventArgs> Overdrawn;
public decimal Balance
{
get { return balance; }
set { balance = value;}
}
public void UpdateBalance(decimal amount)
{
balance += amount;
if (balance < 0)
{
if (Overdrawn != null) Overdrawn(this, EventArgs.Empty);
}
}
}
}
Straightforward, right? If the account balance goes below zero, we fire the Overdrawn event. We hook up listeners to the event like this:
using System;
namespace AddRemoveDelegates
{
class Example
{
public static void account_Overdrawn( object sender, EventArgs e )
{
Console.WriteLine(“Account is overdrawn”);
}
public static void Main(string[] args)
{
Account account = new Account();
// hookup the event listener
account.Overdrawn += account_Overdrawn;
account.UpdateBalance(-10);
}
}
}
For the most part, it’s as easy as that. But, have you ever wondered just what magic is going on inside that call to += ?
Take a moment to consider the Balance property on the Account class we defined above. How do we access it? Well, first we have to back it up with a private field (named balance with a lowercase “b”). Then, we define get and set implementations to control how the private field is accessed and altered. They teach that in Day 1 of a C# programming class.
event members work largely the same way, except the compiler does some work behind the scenes on your behalf. When you declare an event, the compiler gives you a hidden MulticastDelegate field that manages a collection of event listeners. The += and -= operators are implemented to add and remove the handlers from the hidden delegate.
However, you have the ability to explicitly implement your own behavior for += and -= using the keywords add and remove, respectively, just as properties use get and set.
Here’s an alternate implementation of the Account class:
using System;
namespace AddRemoveDelegates
{
public class Account
{
private decimal balance;
// we have to explicitly declare a MulticastDelegate
private EventHandler<EventArgs> onOverdrawn;
// here, we control the adding and removing of listeners
public event EventHandler<EventArgs> Overdrawn
{
add
{
onOverdrawn =
(EventHandler<EventArgs>)Delegate.Combine(onOverdrawn, value);
}
remove
{
onOverdrawn =
(EventHandler<EventArgs>)Delegate.Remove(onOverdrawn, value);
}
}
public decimal Balance
{
get { return balance; }
set { balance = value;}
}
public void UpdateBalance(decimal amount)
{
balance += amount;
if (balance < 0)
{
// we also have to invoke the delegate through the field, now
if (onOverdrawn != null) onOverdrawn(this, EventArgs.Empty);
}
}
}
}
We’ve added a private field of type EventHandler<EventArgs> called onOverdrawn. EventHandler<EventsArgs> is a subtype of System.MulticastDelegate, and this will be our collection of registered event listeners. We’ve also created add and remove implementations for the Overdrawn event. Using static methods Combine and Remove on the Delegate class, we can update the listeners in our MulticastDelegate.
We haven’t gained anything, yet. This is what C# would otherwise give us for free. But, think about the possibilities! Yes! The possibilities!
What possibilities?
Yes, but what’s it good for?
When the only tool you have is a hammer, every problem looks like a nail — and this is a really cool hammer. It’s got what I call “geek appeal”. It’s a clever trick to show other developers so they can bow at your genius. Unfortunately, I’m kind of at a loss for a legitimate use — except maybe for some contrived examples.
I don’t like creating side effects in what is traditionally a simple and well understood concept — the Observer Pattern. Making code do something unexpected is likely to confound clients of your class unless the behavior is very narrow and contained in scope. Why solve a problem by injecting specialized behavior into event management when you can solve it somewhere else?
Okay, logging the adding and removing of listeners might make sense. Perhaps even supporting thread synchronization would make sense (you might want to prevent changes in the MulticastDelegate while an event is in the middle of firing).
Still, it seems like something a junior programmer would do just to show off – but unwittingly invite trouble at the same time.
What do you think? Have you used this before? Can you envision any problems where custom event listener management is the ideal solution?







July 26th, 2007 at 4:22 pm
See http://msdn.microsoft.com/msdnmag/issues/06/11/NETMatters/ for some suggested uses of this feature.
November 29th, 2007 at 12:11 pm
I *hoped* that if I made the custom multicast delegate protected instead of private as you have it, then it would eliminate the problem where an event defined in a base class can’t be raised by a derived class. But it didn’t work. Then I realized something seemed to be missing in your code anyway–while you show how to have += and -= add handlers to the custom list instead of to the private default list, I don’t see anything that tells the application, when you *raise* the event, to call the handler or handlers that are on the custom list rather than continuing to look for them on the default list.
November 29th, 2007 at 1:24 pm
There’s nothing missing. You don’t need to write it because it’s handled for you by .NET. The call to Delegate.Combine() is what builds up the list of handlers into a multicast delegate encapsulated by an EventHandler subtype. It’s that implementation of EventHandler that calls all of the handlers previous registered.
Remember, all we’re doing here is intercepting the calls to add and remove handlers. Actually invoking the handlers when an event is raised is still handled by the framework.
You *could*, if you wanted to, simply add all of your handlers to an ArrayList and not use Delegate.Combine() at all. Then, you could write a custom implementation that iterates over all of the registered handlers (I think that’s the part you thought was missing). That’s a very obscure scenario (perhaps you need to ensure your handlers are called in a particular order). I’d make sure it’s really necessary before having to test and maintain that code.
Good luck. Let me know how it turns out.
September 22nd, 2008 at 7:30 am
It’s a useful pattern in proxy classes where the proxy relays message broadcasts (that come in over TCP, for example) as events. I intercept the add/remove to notify the server whether it needs to broadcast a certain type of messages (no handlers means no need to broadcast, saving bandwidth).
November 3rd, 2008 at 11:34 am
The main purpose of this is really the following:
In C++ we basically had to manage our own lists of function pointers (callbacks) in order to provide for notifications.
In visual basic we had events built in.
So in order to make events easier and more modern and acceptable (probably to convert more VB programmers into C# programmers) events had to be a language feature.
Type safe function pointers (delegates) are used to make this possible.
Built into delegates have invokation lists.
Enter managed code and garbage collection.
Enter you as a component developer.
If you do not declare your delegate as a private member of your class then YOU cannot access it’s invokation list.
If you only declare the event public (and therefore do not have a property) then you are limited by the compiler to the access of that field.
The documentation states that the keyword event limits access to the delegate to using the binary += and -+ operators.
So long story short is this.
Considering the keyword “event” exists is NO excuse to be irrisponsible in coding and lazy.
Not using this pattern is somewhat the same as being irrisponsible with your pointers in a non managed environment.
It is similar to NOT calling delete or not creating a “smart pointer” type situation where you bind delete, removing of a reference counter, and the setting to null all in one step.
What I am saying is this.
If you are going to code a component that offers many events and design that component so that it is going to be heavily used then you don’t want to subject every single one of you clients to surviving generation 1 garbage collection.
If you offer an event and you have subscribers even if this goes out of scope your hidden non accessible invokation lists all still have references to you old clients. They will then NOT be released from garbage collection as soon as they could be. If you combine structures like this in algorithms that utilize a high number of objects or lots of stack space then you are coding yourself into the situation that you do not have the EXPLICIT unbinding of objects in object graphs so that your component can be used in many situations.
Basically this is another way to say it. If your component offers events it is better to also offer IDisposable so that the client code can decide how things are handled. Create a private delegate. Create a public event so clients can subscribe to the notification with the += and -+ operators they are used to. But now VERY important -> in your Dispose enumerate the private delegates invokation list and remove the references to the clients. This is a “TearDown” and the same thing WAS necessary in VB 6 for certain situations (pointers to parent objects in collections as an example with a COM reference counting systems).
This way you can better control and increase the number of objects that will be freed during a lower generation of garbage collection.
Remember just because it is C# and managed code DOES NOT mean you cannot have a memory leak. If you want to expand an object oriented language with “out-going calls” or “events” which is basically implemented with circular referencing then be aware that just because something was automated for you DOES NOT mean you can’t get in trouble.
Keep the private delegate for anytime you feel you need to control the members of the invokation list. I have been stressing just the garbage collection and memory leak but there are other patterns. Use it when you want to dump the list and require new subscriptions from within a server or singleton periodically. Often templating out this pattern without the dispose is my preference NOT because I want to show people what I know but because I want to somewhat document that depending on how my object is used a refactor should take place which includes some code in here. That is not obvious with the use of the public event field.
November 3rd, 2008 at 11:46 am
Also it is more of a direct wrap to do it like this ->
private StateMachineEventHandler canTransition;
public event StateMachineEventHandler CanTranistion
{
add { this.canTransition += value; }
remove { this.canTransition -= value; }
}
which is pretty darn simple considering the power it can give you.
December 10th, 2008 at 6:29 pm
http://www.cosminonea.net/2008/12/10/Subsonic30AndAPossibleSerializationProblem.aspx
This may be one possible good usage.
January 15th, 2009 at 4:23 pm
Thanks for the post. One usage is for debugging. I couldn’t figure out where handlers were getting added and why there seemed to be to many. This technique allows you to put a breakpoint or a Debug.WriteLine in the add {}.
February 26th, 2009 at 12:08 am
Actually, the most common reason to implement an add & remove on an event is for memory consumption. For instance, let’s say you have a type which has a large number of events. If those events are defined with just a public event field, then the type has to allocate store for each delegate, even if no one is subscribed to the event.
For instance, if the type has 15 events, then when you create an instance of the object, it will have 15 pointers to delegates. Most of the events will be null.
Now, if we change the type to use add and remove and implement the add and remove like so:
public EventHandler Click
{
add
{
eventList.Add(ClickKey, value);
}
remove
{
eventList.Remove(ClickKey, value);
}
}
Where eventList is an EventHandlerList, then your type only needs one reference to track all of it’s events instead of 15. If an event is never subscribed to, then it creates no overhead. While the field approach would always require a null pointer.
Btw, add and remove aren’t the only accessors supported by the .net framework. C++ allows you to also define the raise accessor, but it’s not allowed in C#.
September 3rd, 2010 at 10:52 am
Echoing what Hank Hatch says below, using this direct wrap function enables you to wrap built in .NET classes like BackgroundWorker and abstract an Interface for testing purposes (ie generating mocks etc) and using the Decorator Pattern (ref Head First Design Patterns).
Hank Hatch Says:
November 3rd, 2008 at 11:46 am
Also it is more of a direct wrap to do it like this ->
private StateMachineEventHandler canTransition;
public event StateMachineEventHandler CanTranistion
{
add { this.canTransition += value; }
remove { this.canTransition -= value; }
}
which is pretty darn simple considering the power it can give you.