You have added multiple delegates to a single multicast delegate. Each of these individual delegates must fire, 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.
Use the
GetInvocationList
method as shown in Recipe 7.1. This method returns each individual delegate
from a multicast delegate, and by doing so, allows us to invoke each
delegate within an exception handler. The following method creates a
multicast delegate called All
and then uses
GetInvocationList
to retrieve each delegate
individually. Each delegate is then fired within an exception
handler:
using System; using System.Security; public class DelegateUtilities { public void TestIndividualInvokesExceptions( ) { MultiInvoke MI1 = new MultiInvoke(TestInvoke.Method1); MultiInvoke MI2 = new MultiInvoke(TestInvoke.Method2); MultiInvoke MI3 = new MultiInvoke(TestInvoke.Method3); MultiInvoke All = MI1 + MI2 + MI3; int retVal = -1; Console.WriteLine("Invoke individually (handle exceptions):"); foreach (MultiInvoke individualMI in All.GetInvocationList( )) { try { retVal = individualMI( ); Console.WriteLine(" Output: " + retVal); } catch (SecurityException se) { // Stop everything, malicious code may be attempting // to access privileged data break; } catch (Exception e) { // Display (or log) the exception and continue Console.WriteLine(e.ToString( )); } } } }
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( ) { // Simulate an exception being thrown throw (new Exception("Method3")); Console.WriteLine("Invoked Method3"); return (3); } }
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, execution of the delegates
is immediately stopped to prevent a security breach. However, if
another type of Exception
object is thrown, we
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.
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 Recipe 7.1 and Recipe 7.2; see the “Delegate Class” and “Delegate.GetInvocationList Method” topics in the MSDN documentation.