Throwing Exogenous Exceptions

The previous post discussed Lippert’s exception categorization. This post focuses in particular on how methods should throw exogenous exceptions, which, in Lippert’s words, “are the result of untidy external realities impinging upon your beautiful, crisp program logic.”

The exogenous exceptions a method can throw should be treated like part of the method’s signature. C# does not enforce this: any exception can be thrown from any method. To workaround this, I prefer to have all the exceptions thrown from a method, or methods in a single class, inherit from a common type specific to that method or class.

Consider a Connection class that wraps a transport layer connection, such as a TCP or serial socket.

A partial implementation of Connection might look like this:

public class Connection
{
  public void SendData(byte[] data) {...}
  public int ReceiveData(byte[] data) {...}
}

The SendData, ReceiveData, and any other methods of Connection should only throw exceptions of a specific type, such as ConnectionException. It may throw subtypes if suitable, such as ConnectionLostConnectionException or ConnectionTimedOutException, but all exceptions it throws should inherit from ConnectionException.

Any lower level exceptions generated in Connection methods, such as IOException or SocketException, should be wrapped in a ConnectionException, like so:

public void SendData(byte[] data)
{
  try
  {
    // code that sends data here
  }
  catch (IOException ioe)
  {
    throw new ConnectionException("IOException sending data", ioe);
  }
}

Doing this ensures that a user of the Connection class is insulated from exceptions thrown from methods and classes used by the Connection object that the calling code may not know about. The caller only needs to catch ConnectionException. It may also catch specific subtypes if it will handle those exceptions differently.

Subtypes of the base exception type should be created judiciously. A subtype should be created only if calling code might handle that specific exception case differently from other exceptions.

Having a single base exception type that is always thrown by a particular method means that calling code can ensure it catches and handles all exogenous exceptions that could be thrown from a method in a single catch block. This avoids duplication of exception handling code in the caller. It also reduces the temptation to just catch Exception in the caller. This means that any fatal or boneheaded exceptions will not be inadvertently swallowed.

The exceptions thrown by a public method should be listed in its documentation comment. Adding a new exception subtype that is thrown is not a breaking change: callers should be catching the base type and so should handle the new subtype. Changing the base exception type thrown should be treated as a breaking change, the same way that changing the method’s signature should be.

Java, unlike, C#, has what are called “checked exceptions”: exception types thrown from a method must be declared, and calling code must either catch that exception type or also declare that it may throw it. It’s been a while since I’ve written any Java. I remember finding checked exceptions annoying, but I think that my feelings about checked exceptions are worth reconsidering in light of my experience with a language without checked exceptions.

One thought on “Throwing Exogenous Exceptions

Comments are closed.