DekGenius.com
[ Team LiB ] Previous Section Next Section

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

Solution

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("\tOutput: " + 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);
    }
}

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

See Recipe 7.1 and Recipe 7.2; see the "Delegate Class" and "Delegate.GetInvocationList Method" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section