Sun, Aug 8 2004 22:02
Great to see Paul has blogged about Custom events being added to VB.NET. Not that I am biased at all, but it seems that VB has done this better than the C# team, and has included the Raise method, which transposes to the IL fire method as is associated with Custom events. So this is all very cool, adds some new powerful functionality to VB. However there is still room for improvement, IMO.
The approach VB.NET has taken is somewhere between baby steps and catch up football (talked about mixed metaphors ). I think VB.Net has just looked at what C# has had, looked at what the IL specifications allow for, and basically gone with the more functional IL specification. That’s great, but it just doesn’t feel or look like they looked at the whole picture.
The first embarrassment is the clumsy approach one has to take when actually working with the delegates. Paul’s blog shows a good example of the Delegate.Combine calls needed to be used along with casting. A holistic approach would have included simplified syntax to complete that story.
But, in my eyes, the whole custom event need is overly complex for what is usually just a combination of what should be high level language concepts//constructs. To understand what I am ranting about here, it’s probably best if we look at the *WHY* we use custom events. The reasons are typically
- Storage optimization
- Asynchronous events
- Exception handling
- Other action when a delegate is added or removed
Really, the only time you should have to use custom events is case 5 above. For the other patterns, the language should do this menial coding for you.
1. Storage Optimization. Paul provides a good example of this, and this is as is used by WinForms. WinForms however uses a linked list as storage as the reason for the optimization is that there is a belief that usually only a few events will be handled. So a small linked list performs well. A dictionary is probably more suited to a large list, in which case the optimization is not going to be worth the effort, as there will be penalties each time a handler is added/removed and a penalty each time an event is fired. That is, unless the ratio of events to handled event is very high, you end up loosing on the swings anything you made up on the merry-go-round ;)
Still this is a simple pattern that should not require every developer to write all that code each time they want to use it
2. Serialization. Rocky gives a good explanation of this technique. Basically if you have objects that are based on MarshalByRef, you can’t serialize your object, unless you eliminate the event field from being serialized. So you need to store some those delegates in a non serialized field. See Rocky’s modified Custom event code here.
Note: Rocky decided to serialize those event delegates that could be serialized, and not serialize those that were MarshalByRef. Another approach is to not separate the lists, instead not serialize any of the delegates. Which you choose will depend on your architecture.
3. Asynchronous Events. For this pattern you just iterate the invocation list and raise each event on a separate thread.
4. Exception handling. One thing about raising events a lot of people don’t realize is that if any handler throws an unhandled exception, that will stop any other event handlers for that event getting notified. Custom events allow you to code around this by getting the invocation list and wrap each invoke inside a Try Catch block.
5. Taking an action when an event handler is added or removed. This pattern is useful if you have an expensive resource you only want to monitor if a consumer of your component//class wants event notification. A simple example might be directory monitoring. There would be no need to be monitoring a directory for changes unless there is a subscriber to your event.
Of the above, as I mentioned before, only the last case scenario is the one I believe you should have to write code for. All the other cases are really mundane code that the compiler should spit out for you.
Hopefully next version around, VB.Net will look at this and remove the need for all that typing ;) My suggestion would be a CustomEvent attribute(s) or modifiers. Eg:
Public Event Foo As EventHandler
Simple code like that which would apply those common four patterns, or any combination of them. Most of those are simple Boolean switches, except the serialization one which would be, None, All, or NoMarshalByRef.
This isn’t rocket science, and the onus should not be on the VB developer to write all this repedative code, for what is well defined scenarios. The whole concept of RAD is to remove that un-necessary glue code, and instead generate it for us.