I have a lot of UI controller code that looks like this:
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
where
The default
And then you can use it like this:
The only two scenarios where this is worth while is when the
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.
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.
ReplyDeleteI'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 );
}
}