My last post was many months ago, and I’ve been pretty busy here since then. Nobody who starts a blog means to neglect it, even though mostly that’s what happens. I won’t let it go for so long again. =)
Anyway, we were talking about events. Field-like events in particular, and the nastiness of their implementation with respect to the way they use synchronization. Sam, also on my team, has a post that has to do with field-like events, in particular that when you declare them virtual and then override them, you should be a bit careful.
I want to bring up something else about events, which is their performance in space–how much of it they use. On a per-instance basis, each field-like event you declare takes up at least the size of a delegate field. Is that the best you can do?
WinForms doesn’t think so. Take a minute and reflect over System.Windows.Forms.Control and look at the events. There are 68 of them. I won’t list them all here, but they’re things like "Click," "GotFocus," "KeyDown," etc. Now, Control is the fundamental base class for pieces of WinForms UI, and so there are likely to be a lot of instances around. If these events were all field-like, then every piece of UI would be bloated by 68 delegate fields. That’ll add up quickly, but the important bit is that it’s almost all a waste. It’s not the case that control events are frequently subscribed to. Mostly they’re not. You create a button and then you listen for Click and maybe one or two others, but you’re not adding 68 events on the thing.
So what did they do? Well, you can look at the events. They implemented their own accessors, and those accessors, instead of dealing in terms of delegate fields, deal with a list of delegates that have been used so far in that instance.
This strategy allows the list to be sparse.
See, if no one ever calls add for a particular event, then it never gets a list entry. If someone does, then it has a list entry and the total footprint grows by that amount for your instance, but that’s ok. You’re paying for what you’re using!
This is a really good idea, if you’re in the same boat. Of course… you’re probably not. Just having a handful of events won’t warrant this amount of attention, since the container is going to have overhead. And if you implement a lot of events but they’re always used, or if you don’t have a lot of instances, you probably also don’t want to do this. Oh, and if you’re going to try an optimization like this, test it to see if it gets you anything. You don’t want to wind up with an implementation that’s worse than you started with (which is actually not that unlikely–an obvious implementation here uses a Dictionary, but Dictionaries have quite a lot of overhead). You can see that the particular implementation used by System.Windows.Forms.Control has a tiny, hand-crafted list.
So that’s another reason sometimes not to use field-like events.