Recipe9.3.Handling Exceptions Individually for Each Delegate in a Multicast Delegate


Recipe 9.3. Handling Exceptions Individually for Each Delegate in a Multicast Delegate

Problem

You 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.

Solution

Use 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

 public static void TestIndividualInvokesExceptions() {      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("Invoke individually (handle exceptions):");     // Create an instance of a wrapper exception to hold any exceptions     // encountered during the invocations of the delegate instances.     MyExceptionHolderException holderEx = new MyExceptionHolderException();     foreach (MyDelegate instance in allInstances.GetInvocationList())     {         try         {             int retVal = instance();             Console.WriteLine("\tOutput: " + retVal);         }         catch (SecurityException se)         {             // Stop everything; malicious code may be attempting to              // access privileged data.             // Create an EventLog instance and assign its source.             EventLog myLog = new EventLog();             myLog.Source = "MyApplicationSource";             // Write an entry to the event log.              myLog.WriteEntry("Security Failure in MyApplication! " +               se.ToString(),                 EventLogEntryType.Error);              // Rethrow the exception to stop things since it was a              // security failure.              throw;         }         catch (Exception e)         {             // Display (or log) the exception and continue.             Console.WriteLine(e.ToString());               // Add this exception to the exception holder.               holderEx.Add(e);         }     }     // If we caught any exceptions along the way, throw our     // wrapper exception with all of them in it.     if (holderEx.Exceptions.Count > 0)         throw holderEx;  } 

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

 public class MyExceptionHolderException : Exception  {     private List<Exception> _exceptions = null;         public MyExceptionHolderException()         {             _exceptions = new List<Exception>();         }         public List<Exception> Exceptions         {             get { return _exceptions; }         }         public void Add(Exception ex)         {             if (ex == null)                 throw new ArgumentNullException("ex");             _exceptions.Add(ex);         }         public void AddRange(Exception [] exceptions)         {             if (exceptions == null)                 throw new ArgumentNullException("ex");             _exceptions.AddRange(exceptions);          }      } 

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);     } } 

Discussion

If 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 Also

See Recipes 9.1 and 9.2; 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