Making an exception

I don’t normally blog about code. I don’t often make my blog entries into the lists that are so popular on the Code Project. Plenty of people do plenty of that already. Today, I am making an exception to these rules, to talk about exceptions… Nothing I am going to present is rocket-science. This article, is closer to introductory reading for a junior programmer, or to assist someone mentoring one. Although my examples will be in C#, they should apply for any object-oriented language.

Tip 1: Only trap exceptions you are prepared to handle

Here is a bad example that doesn’t do this:

try
{
    DoSomeFunkyMaths();
    DisplayResultsOfFunkyMaths();
}
catch
{
    // We know we may get a div by 0, so ignore exceptions.
    DisplayOurFunkyMathsFailed();
}

Instead, you should write your code as if you are expecting a particular exception.

try
{
    DoSomeFunkyMaths();
    DisplayResultsOfFunkyMaths();
}
catch (DivideByZeroException)
{
    // We know we may get a div by 0, so ignore that exception.
    DisplayOurFunkyMathsFailed();
}

Essentially, this boils down to “Don’t provide a general exception trap because something *might* happen. Trap explicit exceptions that we know the program can handle.
If you are thinking: “But wait! Our program could still fail and we’re not doing anything about it!” you would be right. It is better to fail early and know why you failed, than to fail later without a clue. When you “bury” exceptions in a generalised exception trap, you leave yourself open to a “clueless failure”. Imagine that DoSomeFunkyMaths() includes some writing of data to file. Now it is possible for I/O exceptions to occur as well as the division by zero. With a general exception trap, you will not know that this has failed and when the code subsequently attempts to use the data from the file, you will get unexpected issues.

Tip 2: If you are going to raise your own exceptions, make your own exception type.

Again, here is not what to do:

if (ErrorHasOccurred())
    throw new Exception("Something has gone terribly wrong");

The only way to catch this exception, is to catch all exceptions. If you haven’t figured out what is wrong with this, reread Tip 1 until it sinks in… While I am at it, try and make your messages a little more helpful than “Something has gone terribly wrong”.
In C#, it isn’t hard to make your own exception class. If you cannot be bothered putting much effort in, here’s a simple example.

public class FunkyMathsException : Exception
{
    public FunkyMathsException(String Message)
        : base(String Message)
    { }
}

This is a perfectly acceptable start for your own exception class. Now, code you write that throws exceptions, will use this, instead of the base exception class.

if (ErrorHasOccurred())
    throw new FunkyMathsException("Something has gone terribly wrong");

I still haven’t learnt to put a more meaningful message in. But, at least I can now catch a FunkyMathsException, where I want to, and leave other exceptions well alone.

Tip 3: A generalised exception trap does have one perfectly acceptable use.

I am not an expert on all languages, but normally, an unhandled exception will cause a program to terminate. Depending on the circumstances, this may or may not be acceptable.
Acceptable: Your program is some sort of stateless service that will be restarted via a script or operating system should it stop.
Unacceptable: Your program is a word processor and the user has not saved their document for the last 30 minutes because they are too busy typing away furiously on their “best-seller”
If your program falls into the “unacceptable to unexpectedly stop” category, a global exception handler is the way to go. Save / report the error / do what you have to do… Just be careful to try and not raise an exception. This is serious “infinite loop” / “stack overflow” territory and your program is already on rocky ground.

Tip 4: Exceptions do not cross process boundaries.

I do not know how general this tip is. YMMV. From what I have seen, calling code in a separate library via COM, exceptions do not cross boundaries. The calling code will throw some sort of exception, but most of the specifics to the exception will be lost. It is just best to use other means to relay failure messages back from the library code.

Tip 5: Do not raise a new exception in response to an exception

There may be times when you wish to perform some operation when an exception occurs, but not actually deal with the exception. For instance, the calling code may be better placed to deal with a particular exception but you wish to perform some logging action at the time. If you raise a new exception, you will have lost potentially useful debugging information, such as the call-stack of where the original exception has occurred. Fortunately, most languages provide the ability to “re throw” the exception, simply by using the “throw” keyword by itself.

try
{
    PerformSomeFunkyMaths();
    DisplayResultsOfFunkyMaths();
}
catch (DivideByZeroException)
{
    LogDivByZeroError();
    throw;
}

Tip 6: Exceptions are for exceptions

There is a balancing act between raising / trapping exceptions, or testing conditions with an if statement and acting accordingly. Using if statements increases code complexity and will take some processing time, every time the statement is evaluated. Using / trapping exceptions may simplify the code, but where an exception is raised, they tend to be far more expensive (time-wise). Therefore, using exceptions should be something that is done for the odd occasion where things haven’t gone according to plan. This point is a rather grey area and open to interpretation.