So, my last post talked about delegates. In it, I mentioned some compiler trickery involved in declaring events, but I didn't bother explaining it. After reading it over again, and getting some feedback, I felt bad about glossing over what is pretty much the mainline scenario for delegates. So, what is an event?
An event is kind of like a broadcast. It enables an object to notifiy subscribers when some "event" occurs, give them relevant information about the event, and allow them to do something in response. And, you guessed it, delegates are at the core of making this work.
Fundamentally, events are a callback mechanism, and could be implemented without delegates using anything from raw function pointers to interfaces, and the CLR doesn't keep you from doing either of those, but there's value in a consistent pattern. In fact, the designers of the CLR felt so strongly about the value of this particular pattern, that it is part of the CLI spec (along with properties, another pattern that is implemented by other more fundamental constructs).
So, how do you make an event? Well, in C#, you declare an event like you would declare a field whose type is some delegate and you add the "event" keyword. So, somewhere in a type, you would have something like:
public event EventHandler Click;
Whether or not it's public depends on how you expect the event to be used. EventHandler is a delegate with the following signature:
void EventHandler(object sender, EventArgs e);
This signature is another pattern that I'll talk about later. For now, lets look at what the compiler does for our event declaration. The compiler gives 3 things (if you don't count the things it already did for the delegate EventHandler):
When other code wants to hook up to your event, they use the += operator on your event. This is really syntax sugar for calling the add_Click method. And, conversely the -= operator calls the remove accessor.
Interestingly, you can write your own implementation for the event pattern. You might want to do this to save size in a possibly large tree structure with lots of events at each node. ASP.net does this with controls. Rather than every Control having tons of fields for each event, it has a sparse dictionary of event delegates, that is only populated for events that have "subscribers". With a tree that can easily have thousands of controls per page view, this results in a sizeable savings. How do you do this? Well, in C#, you use the little known syntax:
public event EventHandler Click { add {/* do something with value in here */}
public event EventHandler Click {
add {/* do something with value in here */}
remove {/* do something with value in here */} }
remove {/* do something with value in here */}
}
Looks like a property eh? This causes the compiler not to create the 3 things I mentioned above. Instead, it calls your add and remove accesors to do the adding and removing (via the value keyword just like properties). In it, you can do anything you want, although it's advisable to keep the same semantics as the default implementations.
So, lets talk a little bit about what happens when an event happens and it is called. Let's say that several other classes have registered for your event (via the += syntax or whatever the compiler supports). Inside your class, you simply call the delegate (there's a recommended pattern for this as well). But wait, there's more than 1 subscriber! Remember, delegates aren't just function pointers, and they are more powerful than using interfaces alone. If you'll recall in the last post, I said that when you create a delegate, you're really getting a MulticastDelegate, which tracks an invocation list of delegates to run. (this is why the standard event pattern returns void, otherwise, you've got the weird situation of multiple return values from what appears to be a single call). Under normal circumstances, each delegate in the invocation list is called and execution resumes.
Remember Me
Page rendered at Monday, October 13, 2008 10:02:00 AM (Pacific Standard Time, UTC-08:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.