Lesson 3 - Observer with Multicast Delegates

Now it's time to take one step closer to using events to implement Observer. In the last lesson, we were introduced to delegates and were able to use them to remove some of the work of implementing Observer. However, the implementation still required us to maintain a list of the delegates that needed to be called. In this lesson, we'll discuss a way to let .NET maintain this list for you, and write even less code to implement the Observer pattern.

What's a Multicast Delegate?

The .NET designers built a feature into delegates that makes imlpementation of the Observer pattern much easier. A multicast delegate looks just like any other delegate when you are writing code, but it is actually a list of delegates. You can add and remove delegates to a multicast delegate, and when you invoke the multicast delegate it will automatically call every delegate in its list.

There's nothing different about how you create and use multicast delegates compared to normal delegates. The only difference is you use the static method Delegate.Combine to add a delegate to the list and the Delegate.Remove method to remove a delegate from the list. In VB, Delegate is a keyword, so to actually call the methods you'll have to use the [] brackets to escape the class name:

[Delegate].Combine(...)
[Delegate].Remove(...)

A neat feature of Combine is it doesn't care if the delegate is currently null (Nothing) or empty; it will always return at least a 1-item multicast delegate. Likewise, Remove doesn't behave badly when the delegate is null or doesn't contain the delegate you want to remove. When you invoke a multicast delegate, it will call every delegate in its list. Now that we've reviewed multicast delegates, let's take a look at how we'll use them.

Implementing Observer with Multicast Delegates

If you compare the WeatherStationWithMulticastDelegates project to the one used in the previous lesson, you'll find that there's only a few minor changes. Take a look at TemperatureSensor. Instead of maintaining a list of callbacks, not it has a field _updateCallback of type UpdateCallback. AddCallback and RemoveCallback use the appropriate Combine or Remove method to add/remove callbacks to the multicast delegate _updateCallback. OnTemperatureUpdated no longer needs to loop: it simply invokes the multicast delegate and lets .NET do the work of calling everything in the list.

Final Notes

This was a remarkably short article because multicast delegates aren't used much differently than normal delegates. While it's true that we didn't have to manually maintain a list any more, we still had to provide the AddCallback and RemoveCallback methods. Wouldn't it be nice if something handled all of that for us?

Side Note

There's one small gotcha with multicast delegates. Since you aren't looping over the methods yourself, you aren't in control of what happens when something goes wrong. If one of the delegates throws an exception and it isn't handled, none of the delegates that come after that one in the list will be executed. You can hack around this by using the GetInvocationList method to get the list, manually iterate, and handle excptions that are thrown. Think long and hard before doing this, though. Since you tend to use delegates to allow users to hook their code into yours, you can't reasonably guarantee that you can appropriately handle all exceptions they might through. It's best to stick to the general guideline that delegate callback methods should avoid throwing exceptions.