Recipe 9.3. Handling Exceptions Individually for Each Delegate in a Multicast DelegateProblemYou have added multiple delegates to a single multicast delegate. Each of these individual delegates must be invoked, regardless of whether an unhandled exception is thrown within one of the delegates. But once a delegate in a multicast delegate throws an unhandled exception, no more delegates are fired. You need a way to trap unhandled exceptions within each individual delegate while still allowing the rest of the delegates to fire. SolutionUse the GetInvocationList method as shown in Recipe 9.1. This method returns each individual delegate from a multicast delegate and, by doing so, allows you to invoke each delegate within the try block of an exception handler. The following delegate defines the MyDelegate delegate type: public delegate int MyDelegate( ); The method shown in Example 9-1 creates a multicast delegate called allInstances and then uses GetInvocationList to retrieve each delegate individually. Each delegate is then fired within the try block of an exception handler. Example 9-1. Handling exceptions individually for each delegate in a multicast delegate
The MyExceptionHolderException class is able to have multiple exceptions added to it. It exposes a List<Exception> tHRough the Exceptions property, as shown in Example 9-2. Example 9-2. MyExceptionHolderException class
The following class contains each of the methods that will be called by the MyDelegate multicast delegate instances: 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( ) { // Simulate an exception being thrown. throw (new Exception("Method3")); Console.WriteLine("Invoked Method3"); return (3); } } DiscussionIf an exception occurs in a delegate that is invoked from within a multicast delegate and that exception is unhandled, any remaining delegates are not invoked. This is the expected behavior of a multicast delegate. However, in some circumstances, you'd like to be able to handle exceptions thrown from individual delegates and then determine at that point whether to continue invoking the remaining delegates. In the TestIndividualInvokesExceptions method of this recipe, if an exception SecurityException is caught, it is logged to the event log and the exception is rethrown. However, if another type of Exception object is thrown, you just display or log it and continue invoking delegates. This strategy allows for as fine-grained handling of exceptions as you need. Note that, if you rethrow an exception, the exception will be bubbled up to the next enclosing exception handler. If the next outer exception handler is outside of the loop used to iterate through each delegate object returned by the GetInvocationList method, any remaining delegates will not be invoked. One way to deal with this is to store all of the exceptions that occur during delegate processing, then wrap all of the exceptions encountered during processing in a custom exception. After processing completes, throw the custom exception. See the MyExceptionHoldingException class in the Solution. By adding a finally block to this try-catch block, you can be assured that code within this finally block is executed after every delegate returns. This technique is useful if you want to interleave code between calls to delegates, such as code to clean up objects that are not needed or code to verify that each delegate left the data it touched in a stable state. See AlsoSee Recipes 9.1 and 9.2; see the "Delegate Class" and "Delegate.GetInvocationList Method" topics in the MSDN documentation. |