Tuesday, 7 June 2011

Execute Around Exception

I have a lot of UI controller code that looks like this:

try
{
// Do something that might throw is is probably a single line of code
}
catch (Exception e)
{
// Notify the user of the problem.
}

This is 8 lines of code for a single line operation. The common code that notifies the user of the error, commonly just a message box, can easily be refactored into a single method. The problem is the try/catch block. So I started wondering if I could reduce it somehow. I would be so much nicer if I could write:

ExecuteSafely(delegate
{
// Do something that might throw is is probably a single line of code
});

where ExecuteSafely takes care of the boiler plate code. I started playing around with Execute Around Method ideas and came up with this:

public class ExecuteAroundException
{
private readonly Action<Exception> exceptionHandler;

public ExecuteAroundException()
: this(delegate { })
{
}

public ExecuteAroundException(Action<Exception> exceptionHandler)
{
this.exceptionHandler = exceptionHandler;
}

public void ExecuteSafely(Action action)
{
try
{
action();
}
catch (Exception e)
{
exceptionHandler(e);
}
}
}

The default exceptionHandler does nothing. It just swallows the exception. The chances of you wanting to use it are therefore slim. However you can provide a delegate that handles the exception:

ExecuteAroundException x = new ExecuteAroundException(delegate(Exception e)
{
// Notify the user of the problem.
});

And then you can use it like this:
       
x.ExecuteSafely(delegate
{
// Do something that might throw is is probably a single line of code
});

The only two scenarios where this is worth while is when the ExecuteAroundException instance is created once at class scope or created in a factory method. Otherwise you end up with almost as much code.

1 comment:

  1. Like the idea of ExecuteSafely, although not convinced about the ExecuteAroundException, although that does allow it to be passed around as a sort of policy object.

    I've had a play around with this, and for my purposes come up with something like the stuff below:


    // Provides primatves for further development
    public static class XH
    {

    // Execute Safely

    public static void ExecuteSafely< TException >(
    Action action,
    Action< TException > eh )
    where TException : Exception
    {
    try
    {
    action(null);
    }
    catch( TException ex )
    {
    eh( ex );
    }
    }

    public static void ExecuteSafely(
    Action action )
    {
    Action< Exception > eh = NoOpExceptionHandler;

    ExecuteSafely( action, eh );
    }


    // potentially could have versions taking multiple TException types for different actions, but becomes unwieldy very quickly.
    // e.g. to perform logging on all exceptions to a DB, but do something more clever on a DBGoneAwayException.
    public static TReturnType ExecuteSafely< TReturnType, TException >(
    Func action,
    Action< TException > eh )
    where TException : Exception
    {
    try
    {
    return action(null);
    }
    catch( TException ex )
    {
    eh( ex );

    return default(TReturnType);
    }
    }


    public static TReturnType ExecuteSafely(
    Func action )
    {
    Action< Exception > eh = NoOpExceptionHandler;

    return ExecuteSafely( action, eh );
    }


    // Execute

    public static void Execute< TException >(
    Action action,
    Func< TException > eh )
    where TException : Exception
    {
    try
    {
    action(null);
    }
    catch( TException ex )
    {
    if( eh( ex ) )
    {
    throw;
    }
    }
    }

    public static void Execute(
    Action action )
    {
    Func< Exception > eh = delegate( Exception ex)
    {
    NoOpExceptionHandler( ex );
    return false;
    }

    Execute( action, eh );
    }


    // potentially could have versions taking multiple TException types for different actions, but becomes unwieldy very quickly.
    // e.g. to perform logging on all exceptions to a DB, but do something more clever on a DBGoneAwayException.
    public static TReturnType ExecuteSafely< TReturnType, TException >(
    Func action,
    Func< TException > eh )
    where TException : Exception
    {
    try
    {
    return action(null);
    }
    catch( TException ex )
    {
    if( eh( ex ) )
    {
    throw;
    }
    }
    }


    public static TReturnType ExecuteSafely(
    Func action )
    {
    Func< Exception > eh = delegate( Exception ex)
    {
    NoOpExceptionHandler( ex );
    return false;
    }

    return Execute( action, eh );
    }


    // Common Exception handling Policies
    public static void NoOpExceptionHandler( Exception ex )
    {
    }
    }


    Where I've used this I've provided local ExecuteSafely in the class that knows how to handle the exception:

    class AboutBox : Form
    {
    private btnExplode_Click( ... )
    {
    ExecuteSafely( delegate {
    throw new Exception();
    });
    }


    public static void ExecuteSafely(
    Action action )
    {
    Action< Exception > eh = delegate( Exception ex ) {
    MessageBox.Show( this, "It's broken" );
    };

    XH.ExecuteSafely( action, eh );
    }

    }

    ReplyDelete