This is the last article in a series about exceptions:
If the code in the try block needs tidying up (e.g. it opens files, that will need closing at some point), then the optional finally block is a suitable place to do this. It lives after the last catch block that is attached to the try block (or directly after the try block if it has no catch blocks).
When the try block finishes normally, without any exceptions, the code in the finally block is executed once the try block has finished. If execution leaves the try block due to an exception, the code inside any matching catch block is executed, and then code inside the finally block.
So the catch block is to handle the exception, and the finally block is to tidy up after the execution of the try block.
What should go into a catch block?
The purpose of a catch block is to try to get the system out of the bad state it’s in so that it can try to do some more work. The bare minimum is usually to log the fact that an exception has occurred, with information that would be useful later when someone (possibly you) needs to debug things.
Given that things are already in a bad state, the benefits of lots of tidying up, logging etc. need to be weighed against the costs and risks of the tidying up code having an error, throwing a fresh exception etc. (This is a turtles all the way down / who guards the guards? kind of problem.)
As I mentioned earlier, sometimes a catch block will deliberately throw an exception as part of helping some other code deal with the problem. It might store the original exception as a property of the new exception, and it might throw an exception that’s a different type to the original.
If you do this, please be careful about accidentally throwing away information, such as the call stack. See this Stack Overflow article about correct re-throwing.
Pattern stuff – chain of responsibility
For those who are into software design patterns, exceptions are an example of the chain of responsibility pattern. In the chain of responsibility, there is a source of command objects (in this case the throw statement creating an exception), and an ordered list or pipeline of processors (the present or missing catch blocks). The source is free to create the command object without worrying about the details of the pipeline or processors.
The throw statement doesn’t need to know which code where will catch the exception. The frames in the call stack at the point that the exception is thrown form the ordered list, where each element in the list is able to have a processor (a matching catch block) or to pass the command object on to the next element in the list (by having no catch block, or only catch blocks that don’t match the exception).
When should you throw an exception?
This is the hard bit, as I mentioned at the very beginning, because the answer is “it depends”. If you take a database look-up as an example, there are often these classes of outcome:
- 0 results
- 1 result
- 2 or more results
- A problem with the database e.g. it can’t parse the query
Usually the last one is a good candidate for an exception – this is something that isn’t expected to happen, and probably can’t be dealt with by the current method.
1 result is often well-behaved and a success, so I’ll ignore it.
The other two are interesting, because they might be OK but they might not be. 0 results might be OK if you want to update something if it already exists but create it if it doesn’t – you need to check if it exists and might get back 0 results. However, 0 results might be bad if the value you’re using for the look-up is a primary key value from the table you’re querying – data has disappeared unexpectedly.
Similarly, 2 or more results might be OK or might not. If you’re looking for all the items a customer has purchased, you could easily get many results. If you’re looking something up by its primary key, you wouldn’t expect more than 1 result, so 2 or more results is an unexpected error.
The kinds of question to ask to help you know when to throw an exception and when to just return an error or some null result are things like:
- How expected is this error?
- How bad is it if this error occurs?
- Do I know where the code is that can handle this error? If so, how close is it to where the error is discovered?
I can’t give you a simple and 100% reliable series of steps to help you know when to throw an exception.
It’s worth looking at other code for inspiration. If you consider looking up values in dictionaries (which is similar to the database look-up above), there are two alternative approaches.
One is to simply use the value as an index into the dictionary, e.g. MyDictionary. If there is a value associated with the key 12, this will be returned, but if the key 12 is missing from the dictionary a KeyNotFound exception is thrown.
The other approach is to do bool foundSomething = MyDictionary.TryGetValue(12, out string returnedValue). This will return true if the key 12 is present and put its value in the out parameter returnedValue. If 12 is missing it will return false.
The benefit of the first approach is that the calling code is shorter and more fluent, but will blow up if the key is missing. The benefit of the second approach is more robust and clearer code, but that is more verbose.
I’m not saying one is better than the other, but they are suited to different contexts and so you need to look at your context.
At the simplest end of the spectrum, simply returning true / false for success or failure is often good enough. Or returning a string that holds an error message if there was an error, but is null if there’s no error.
If you want to return a result and some indication of an error, then the TryGetValue model above is a good one. The indication of an error is the method’s return value, and the main value is passed back via an out parameter. Please don’t use magic values, e.g. the method returns only an integer which is the main result unless it has the value -9999, because that indicates an error. They are brittle to future changes, easy to get wrong and hard to understand quickly.
In the past I’ve worked with C# code that defined its own wrapper so that methods could return:
- Success / failure flag
- An error message
- The main result
in one object. This is fairly straightforward to do with generics, but doesn’t give you the catch-block-seeking-missile behaviour for free – you have to implement this yourself if you want it.
I’ve also worked with C code (yes, not C#) that implemented its own version of exceptions using goto statements (sanitised behind #defines etc.), with a status object declared high up in the code that could store the stack trace on error. It worked very well, but took discipline by programmers. If your language supports exceptions I suggest you use them rather than trying to implement your own version.
Testing of errors in general is often both overlooked but very important, and exceptions are no different. It can also be hard to do, because e.g. the database needs to appear to have crashed. It’s important because writing code to handle errors correctly is also often overlooked, hard but very important.
As this video shows, failing to handle errors properly can have big real-world consequences, and testing can help with that: