Recipe9.1.Controlling When and If a Delegate Fires Within a Multicast Delegate


Recipe 9.1. Controlling When and If a Delegate Fires Within a Multicast Delegate

Problem

You have combined multiple delegates to create a multicast delegate. When this multicast delegate is fired, each delegate within it is fired in turn. You need to exert more control over such things as the order in which each delegate is fired, firing only a subset of delegates, or firing each delegate based on the success or failure of previous delegates.

Solution

Use the GetInvocationList method to obtain an array of Delegate objects. Next, iterate over this array using a for (or foreach if order is irrelevant) loop. You can then invoke each Delegate object in the array individually and optionally retrieve its return value.

In .NET, all delegate types support multicastthat is, any delegate instance can invoke multiple methods each time it is itself invoked if it has been set up to do so. In this recipe, we use the term multicast to describe a delegate that has been set up to invoke multiple methods. The following delegate defines the MyDelegate delegate type:

 public delegate int MyDelegate(); 

The following method creates a multicast delegate called allInstances and then uses GetInvocationList to allow each delegate to be fired individually, in reverse order:

 public static void InvokeInReverse() {     MyDelegate myDelegateInstance1 = new MyDelegate(TestInvoke.Method1);     MyDelegate myDelegateInstance2 = new MyDelegate(TestInvoke.Method2);     MyDelegate myDelegateInstance3 = new MyDelegate(TestInvoke.Method3);     MyDelegate allInstances =             myDelegateInstance1 +             myDelegateInstance2 +             myDelegateInstance3;     Console.WriteLine("Fire delegates in reverse");     Delegate[] delegateList = allInstances.GetInvocationList();     for (int counter = delegateList.Length - 1; counter >= 0; counter--)     {         ((MyDelegate)delegateList[counter])();     } } 

As the following methods demonstrate by firing every other delegate, you don't have to invoke all of the delegates in the list. Note that, in this example, the multicast delegate is constructed in one method (InvokeEveryOtherSetup) and used in another method (InvokeEveryOtherImpl). You might be wondering what would happen if the MyDelegate parameter passed to InvokeEveryOtherImpl were a unicast delegate and you called GetInvocationList on it. Have no fear: GetInvocationList will happily give you a list of one delegate instance:

 public static void InvokeEveryOtherSetup() {     MyDelegate myDelegateInstance1 = new MyDelegate(TestInvoke.Method1);     MyDelegate myDelegateInstance2 = new MyDelegate(TestInvoke.Method2);     MyDelegate myDelegateInstance3 = new MyDelegate(TestInvoke.Method3);     MyDelegate allInstances =             myDelegateInstance1 +             myDelegateInstance2 +             myDelegateInstance3;     InvokeEveryOtherImpl(allInstances); } public static void InvokeEveryOtherImpl(MyDelegate delegateInstance) {     Delegate[] delegateList = delegateInstance.GetInvocationList();     // See if there are any delegates hooked up.     if (delegateList.Length > 0)     {         Console.WriteLine("Invoke every other delegate");         for (int counter = 0; counter < delegateList.Length; counter += 2)         {             // Invoke the delegate.             int retVal = ((MyDelegate)delegateList[counter])();         }     } } 

The following class contains each of the methods that will be called by the MyDelegate multicast delegate allInstances:

 public class TestInvoke {     public static int Method1( )     {         Console.WriteLine("Invoked Method1");         return (1);     }     public static int Method2( )     {         Console.WriteLine("Invoked Method2");         return (2);     }     public static int Method3( )     {         Console.WriteLine("Invoked Method3");         return (3);     } } 

The following delegate defines the MyDelegate delegate:

  public delegate bool MyDelegateTF( ); 

It is also possible to decide whether to continue firing delegates in the list based on the return value of the currently firing delegate. The following method fires each delegate, stopping only when a delegate returns a false value:

 public static void InvokeWithTest() {     MyDelegateTF myDelegateInstanceTF1 =         new MyDelegateTF(TestInvokeTF.Method1);     MyDelegateTF myDelegateInstanceTF2 =         new MyDelegateTF(TestInvokeTF.Method2);     MyDelegateTF myDelegateInstanceTF3 =         new MyDelegateTF(TestInvokeTF.Method3);     MyDelegateTF allInstancesTF =             myDelegateInstanceTF1 +             myDelegateInstanceTF2 +             myDelegateInstanceTF3;     Console.WriteLine(         "Invoke individually (Call based on previous return value):");     foreach (MyDelegateTF instance in allInstancesTF.GetInvocationList())      {         // This break is not required; it is an optimization to prevent the         // loop from continuing to execute.         if (!instance())             break;     } } 

The following class contains each of the methods that will be called by the MyDelegateTF multicast delegate allInstancesTF:

 public class TestInvokeTF {     public static bool Method1( )     {         Console.WriteLine("Invoked Method1");         return (true);     }     public static bool Method2( )     {         Console.WriteLine("Invoked Method2");         return (false);     }     public static bool Method3( )     {         Console.WriteLine("Invoked Method3");         return (true);     } } 

Discussion

A delegate, when called, will invoke all delegates stored within its invocation list. These delegates are usually invoked sequentially from the first to the last one added. With the use of the GetInvocationList method of the MulticastDelegate class, you can obtain each delegate in the invocation list of a multicast delegate. This method accepts no parameters and returns an array of Delegate objects that corresponds to the invocation list of the delegate on which this method was called. The returned Delegate array contains the delegates of the invocation list in the order in which they would normally be called; that is, the zeroth element in the Delegate array contains the Delegate object that is normally called first.

This application of the GetInvocationList method gives you the ability to control exactly when and how the delegates in a multicast delegate are invoked and allows you to prevent the continued invocation of delegates when one delegate fails. This ability is important if each delegate is manipulating data and one of the delegates fails in its duties but does not throw an exception. If one delegate fails in its duties and the remaining delegates rely on all previous delegates to succeed, you must quit invoking delegates at the point of failure. Note that an exception will force the invocation of delegates to cease, but throwing an exception is an expensive process. This recipe handles a delegate failure more efficiently and also provides more flexibility in dealing with these errors. For example, you can write logic to specify which delegates are to be invoked, based on the performance of previously invoked delegates.

See Also

See Recipes 9.2 and 9.3; see the "Delegate Class" and "Delegate.GetInvocationList Method" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

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