Events and Delegates

Events and Delegates

The managed events I’m talking about here are not synchronization elements, similar to Win32 event objects. Rather, they more closely resemble Microsoft Visual Basic events and On<event> functions. Managed events provide a means to describe asynchronous execution of methods, initiated by certain other methods.

The general sequence of activities is illustrated in Figure 12-1. A program unit—known as the publisher, or source, of an event—defines the event. We can think of this program unit as a class, for the sake of simplicity. Other program units (classes)—known as subscribers, or event listeners, or event sinks—define the methods to be executed when the event occurs and pass this information to the event publisher. When the event publisher raises, or fires, the event by calling a special method, all the subscriber’s methods associated with this event are executed.

Figure 12-1 The interaction of an event publisher and subscribers.

In a nutshell, to implement a managed event we need an entity that can collect the callback methods (event handlers) from the event subscribers and execute these methods when the publisher executes a method that signifies the event.

We have a type (a class) designed to do exactly that: the delegate type, which is discussed in Chapter 6, “Namespaces and Classes.” As you might remember, delegates are classes derived from the class [mscorlib]System.MulticastDelegate and play the role of “politically correct” function pointers in the managed world. The actual function pointer to a delegated method is passed to the delegate as an argument of its constructor, and the delegated method can subsequently be executed by calling the delegate’s Invoke method.

What Chapter 6 doesn’t mention is that several delegates can be aggregated into one delegate. Calling the Invoke method of such an aggregated delegate invokes all the delegated methods that make up the aggregate—which is exactly what we need to implement an event.

The [mscorlib]System.MulticastDelegate class defines the virtual methods CombineImpl and RemoveImpl, adding a delegate to the aggregate and removing a delegate from the aggregate, respectively. These methods are defined in Mscorlib.dll as follows. (I have omitted the resolution scope [mscorlib] of delegate types here because the methods are defined in the same assembly; this doesn’t mean you can omit the resolution scope when you refer to these types in your assemblies.)

    .method  family  hidebysig  final  virtual                instance  class  System.Delegate                CombineImpl(class  System.Delegate  follow)  cil  managed     {  ...  }     .method  family  hidebysig  final  virtual                instance  class  System.Delegate                RemoveImpl(class  System.Delegate  'value')  cil  managed     {  ...  }

The methods take the object reference to the delegate being added (or removed) as the argument and return the object reference to the aggregated delegate. The parameter and return type of both methods is System.Delegate rather than System.MulticastDelegate, but this isn’t contradictory: System.MulticastDelegate is derived from System.Delegate and hence can be used in its stead.

The principle of the delegate-based implementation of an event is more or less clear. Each event subscriber creates a delegate representing its event handler and then subscribes to the event by combining the handler delegate with the aggregate delegate, held by the event publisher. To raise the event, the publisher simply needs to call the Invoke method of the aggregate delegate, and everybody’s happy.

One question remains, though: what does the publisher’s aggregate delegate look like before any event subscriber has subscribed to the event? The answer is, it doesn’t exist at all. The aggregate delegate is a result of combining the subscribers’ handler delegates. As long as there are no subscribers, the publisher’s aggregate delegate does not exist. This poses a certain problem: CombineImpl is an instance method, which has to be called on the instance of the aggregated delegate, and hence each subscriber must worry about whether it is the first in line (in other words, whether the aggregated delegate exists yet). That’s why the subscribers usually use the static methods Combine and Remove, inherited by System.MulticastDelegate from System.Delegate:

        .method  public  hidebysig  static  class  System.Delegate                    Combine(class  System.Delegate  a,                          class  System.Delegate  b)           {  ...  }         .method  public  hidebysig  static  class  System.Delegate                    Remove(class  System.Delegate  source,                         class  System.Delegate  'value')           {  ...  }

If one of the arguments of these methods is a null reference, the methods simply return the non-null argument. If both arguments are null references, the methods return a null reference. If the arguments are incompatible—that is, if the delegated methods have different signatures—Combine, which internally calls CombineImpl, throws an Argument exception and Remove, which internally calls RemoveImpl, simply returns the aggregated delegate unchanged.

In general, delegates are fascinating types, with more features than this book can discuss. The best way to learn more about delegates first-hand is to disassemble Mscorlib.dll and have a look at how System.Delegate and System.MulticastDelegate are implemented and used. The same advice is applicable to other Microsoft .NET Framework classes you happen to be interested in: when in doubt, disassemble the respective DLL and see for yourself.

Events, of course, can be implemented without delegates. But given the functionality needed to implement events, I don’t see why anyone would waste time on an alternative implementation when the delegates offer a complete and elegant solution.

Managed Synchronization Elements

You’re probably wondering whether managed code has any elements equivalent to the synchronization events and APIs of the unmanaged world. It does, although this aspect is unrelated to the events discussed in this chapter. The synchronization elements of the managed world are implemented as classes of the .NET Framework class library. You can learn a lot about them by disassembling Mscorlib.dll and having a look at the namespace System.Threading—and especially at the WaitHandle class of this namespace. (You’ve already encountered the System.Threading.WaitHandle class in the discussion of asynchronous invocation of delegates in Chapter 6.) The WaitHandle class is central to the entire class system of the System.Threading namespace and implements such methods as WaitOne, WaitAll, and WaitAny. Sounds familiar, doesn’t it? The AutoResetEvent, ManualResetEvent, and Mutex classes, derived from the WaitHandle class, are also worth a glance.



Inside Microsoft. NET IL Assembler
Inside Microsoft .NET IL Assembler
ISBN: 0735615470
EAN: 2147483647
Year: 2005
Pages: 147
Authors: SERGE LIDIN

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net