A Pattern for Handling Exogenous Exceptions Code

I have found myself writing quite a few classes with the following properties:

  • The class has multiple methods that implement operations using other, private, objects.
  • Those private objects can throw exogenous exceptions (e.g. TcpClient), and
  • Exceptions thrown from those objects need to be handled in mostly the same way

To avoid duplicating exception handling code in classes like these, I find it’s convenient to write a private method to do the error handling. This method takes as an argument a delegate that it executes within a try/catch block. It may also take in some metadata about the delegate, such as an operation name, for logging and exception reporting purposes.

Each public method of the classes just calls this method, passing in the delegate to execute and any necessary metadata.

Below is an example with arbitrary class names (and also using a Log method to stand in for the actual logging methods):

public class Foo
{
  private Bar m_bar;
  private bool m_ioFlag;

  private ResultType DoOperation<ResultType>(string operationName, Func<ResultType> operation)
  {
    try
    {
      Log($"Operation {operationName} started.");
      var result = operation();
      Log($"Operation {operationName} completed successfully.")
      return result;
    }
    catch(IOException ioe)
    {
      Log($"IOException during {operationName} operation", ioe);
      m_ioFlag = false; // example code to clean up internal state in response to exception
      throw new FooException($"IOException during {operationName} operation.", ioe);
    }
    catch(BarException be)
    {
      Log($"BarException during {operationName} operation", be);
      m_bar.Reset(); // example code to clean up internal state in response to exception
      throw new FooException($"BarException during {operationName} operation.", be);
    }
  }

  public ResultType DoOperationOne()
  {
    return DoOperation(nameof(DoOperationOne),
    () => 
    {
      // implementation of DoOperationOne here, which uses m_bar and returns a Result
    });
  }

  public ResultType DoOperationTwo()
  {
    return DoOperation(nameof(DoOperationTwo),
    () => 
    {
      // implementation of DoOperationTwo here, which uses m_bar and returns a Result
    });
  }
}

The Foo class has two public methods, DoOperationOne and DoOperationTwo. Both operations make use of a private instance of Bar, whose methods may throw a BarException or an IOException. Both just pass the operation name (using the nameof operator) to the DoOperation method. The DoOperation logs that the operation start and finish, and contains the catch blocks for handling exceptions thrown by m_bar by cleaning up the Foo object’s state and then wrapping those exceptions in a FooException and rethrowing.

Following this pattern has several advantages. First, it avoids duplication of logging and error handling code, and ensures that all operations do their logging and error handling in a consistent way. Second, it enforces the advice in my previous post to have only a single exogenous exception type be thrown from each method. Third, it implements the single responsibility principle: the DoOperation method is responsible for error handling, and each of the other methods are only responsible for what is specific to that method.