[ Team LiB ] |
Recipe 7.1 Controlling when and if a Delegate Fires Within a Multicast DelegateProblemYou have added 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. SolutionUse the GetInvocationList method to obtain an array of Delegate objects. Next, iterate over this array using a for loop. You can then invoke each Delegate object in the array individually and optionally retrieve its return value. The following method creates a multicast delegate called All and then uses GetInvocationList to allow each delegate to be fired individually, in reverse order: public void InvokeInReverse( ) { MultiInvoke MI1 = new MultiInvoke(TestInvoke.Method1); MultiInvoke MI2 = new MultiInvoke(TestInvoke.Method2); MultiInvoke MI3 = new MultiInvoke(TestInvoke.Method3); MultiInvoke All = MI1 + MI2 + MI3; Console.WriteLine("Fire delegates in reverse"); Delegate[] Delegates = All.GetInvocationList( ); for (int counter = Delegates.Length - 1; counter >= 0; counter--) { ((MultiInvoke)Delegates[counter])( ); } } The following method fires every other delegate, starting with the first delegate in the list: public void InvokeEveryOther( ) { MultiInvoke MI1 = new MultiInvoke(TestInvoke.Method1); MultiInvoke MI2 = new MultiInvoke(TestInvoke.Method2); MultiInvoke MI3 = new MultiInvoke(TestInvoke.Method3); MultiInvoke All = MI1 + MI2 + MI3; Delegate[] Delegates = All.GetInvocationList( ); Console.WriteLine("Fire every other delegate"); for (int counter = 0; counter < Delegates.Length; counter += 2) { ((MultiInvoke)Delegates[counter])( ); } } In .NET, all delegates are implicitly multicast—that is, any delegate can invoke multiple methods each time it is itself invoked. 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 MultiInvoke delegate: public delegate int MultiInvoke( ); The following class contains each of the methods that will be called by the MultiInvoke multicast delegate: 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); } } 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 void InvokeWithTest( ) { MultiInvokeTF MI1 = new MultiInvokeTF(TestInvokeTF.Method1); MultiInvokeTF MI2 = new MultiInvokeTF(TestInvokeTF.Method2); MultiInvokeTF MI3 = new MultiInvokeTF(TestInvokeTF.Method3); MultiInvokeTF All = MI1 + MI2 + MI3; bool retVal = true; Console.WriteLine( "Invoke individually (Call based on previous return value):"); foreach (MultiInvokeTF individualMI in All.GetInvocationList( )) { if (retVal) { retVal = individualMI( ); } else { // This break is not required; it is an optimization to // prevent the loop from continuing to execute. break; } } } The following delegate defines the MultiInvokeTF delegate: public delegate bool MultiInvokeTF( ); The following class contains each of the methods that will be called by the MultiInvokeTF multicast delegate: 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); } } DiscussionA delegate, when called, will invoke all delegates stored within its invocation list. These delegates are invoked sequentially from the first to the last one added. Once the multicast delegate is called, you cannot change when—or if—any delegate in the list is called. Fortunately, 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 us the ability to control exactly when and how the delegates in a multicast delegate are invoked and allows us 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 AlsoSee Recipe 7.2 and Recipe 7.3; see the "Delegate Class" and "Delegate.GetInvocationList Method" topics in the MSDN documentation. |
[ Team LiB ] |