Sunday 25 March 2007

Exception handling: Catch, throw or let it go?

When you think about it, structured exception handling is a powerful yet reasonably straightforward mechanism for...well for handling exceptions in a structured way. You call a method and it either throws an exception or it doesn't. If it does, you have 3 choices:

  • Rethrow the exception
  • Throw a different exception
  • Catch the exception and continue processing
There are no chiselled, marble tablets defining the universal laws of exception handling. There is no perfect strategy for dealing with every possible exceptional scenario. However, I have seen enough poorly implemented error handling strategies to at least recommend a few guidelines here.

Be careful what you catch
Any catch blocks following Catch(System.Exception) will be ignored because System.Exception is the base class for every other exception type. Luckily, the compiler does not allow this anyway and raises an exception of its own if you decide to ignore this advice.

Only catch an exception if you expected it and you know what to do about it. Never use the construct shown in Snippet 2 in a class library. The way an exception is handled should always be the responsibility of the calling application.

A System.Exception should never be caught unless it is rethrown. This rule should always be followed for class libraries. Within an application, the application policy will determine whether a System.Exception should be rethrown.

Be careful what you throw
Never throw System.Exception; take care when throwing any other exception base class types. If you are defining your own exceptions, then think carefully about its base class. For example, if you derive an exception type from ArgumentException, then any code that already catches ArgumentException will catch your new exception type. This may or may not be a problem based on how you (or someone else) is handling the exception.

All custom exception types should end with 'Exception'. e.g. NumberTooLargeException, NameNotFoundException etc.

Use the recovery position
So you have a class library and an exception occurs. You want to leave the responsibility of handling the exception to the calling application but you also want to make sure that your system is not left in an unrecoverable state. So what do you do (please see Snippet 1)?

 Snippet 1

 public void LibraryMethod()
 {
     try {
         //Method logic here
     }
     catch {
         //Handle system consistency here
         throw;
     }
 }

Basically, all possible exceptions are caught, the system's state is verified and the same exception that originally ocurred is rethrown. It may be appropriate to restore consistency in a Finally block depending on the context of your work.

Please note that using throw ex; instead of throw; causes the stack trace to be reset here.

Avoid boilerplate code
It's easy to get into the habit of adding Try/Catch/(Finally) blocks to each method that you write but this really is not the way to approach structured exception handling. I have worked on projects where the following pattern is used with alarming regularity:

 Snippet 2

 public void ProcessData(int firstNum, int secondNum)
 {
     try {
         //Method logic here
     }
     catch (Exception ex) {
         //Log error here
     }
 }

So any exception is caught, logged and discarded. The application continues (most probably in an inconsistent state) and the user/developer remains blissfully unaware until they make a conscious decision to check the logging device and find entries representing errors.

Validate parameters
Where you are creating a library that will be used by other developers, it is prudent to validate the parameters of all public and protected methods. This has a number of benefits:
  • The caller is informed if invalid parameters are supplied
  • The code is more likely to run consistently with valid data
  • Problems are identified quickly, less code is executed with invalid data
  • Effort to rollback to a consistent state may be avoided
If a problem is detected, throw an exception that derives from ArgumentException; the most notable being ArgumentNullException and ArgumentOutOfRangeException. If these are not appropriate then consider throwing a custom exception derived from ArgumentNullException or ArgumentNullException itself. Snippet 3 illustrates the process.

 Snippet 3

 public int DivideNumbers(int numerator, int denominator)
 {
     //Validate denominator to ensure non-zero value.
     if (denominator == 0)
     {
         throw new ArgumentOutOfRangeException(
             "denominator", "Denominator cannot be zero");
     }

     return numerator/denominator;
 }

Be aware that arguments passed to methods by reference may be changed after validation has occurred (e.g. in a multi-threaded application). In these cases it may be wise to create a copy of the argument(s) and operate exclusively on this copy within the method.

In the example shown, the denominator is validated against a value of zero. An alternative here would be to use a Catch block to trap this occurence. The actual method used depends on how often the situation is likely to occur. The validation as shown is fine but this check will be performed on each pass through the method. If the possibility of denominator being zero is rare then it may be more prudent to use a Catch block and rethrow the exception for improved performance.

And Finally...
A Finally block is guaranteed to run whether an exception occurs or not. It is therefore the logical place to add clean-up code. Beware that any exceptions raised within a Finally block will hide details of any exception that was raised in the corresponding Try block so try to avoid them.

Structured exception handling provides a consistent framework for dealing with exceptional situations. Taking an umbrella with you on a winter's day stroll adds a little overhead to your journey but it also prevents you from getting soaked if the heavens open. Handle exceptions in a similar way; prepare for what may happen before you start your development journey and you will never find yourself without an umbrella if something unexpected happens.

6 comments:

Anonymous said...

There is one pros for using Try/Catch/(Finally) blocks with logging everything without throwing exceptions... The customer thinks for a long time that application work just great. But of curse it is a short time benefit ;)

Unknown said...

Take a look on Effective Java Exceptions article. Even if it focuses on Java exceptions, 95% of the article treats about common exceptions problems. Especially worth to read about is fault barrier pattern.

faf said...

In case of action which you can take when exception occurs in my opinion it is good to underline fact that only re-trowing exception is no good idea (better is to do not write try-catch-[finally] statement there. If you would like to log that exception occurs re-trowing the same exception is OK. However when we case re-throwing exceptions we have two possible ways. We can use inner exception mechanism, or throw another exception - replace original exception - instead of original one. Each way is good in different situation but it is good to know about possible ways what how to deal with exceptions.
Another situation when structural exception handling is good is switch statements when in place default clause we can throw InvalidOperationException
to indicate unsuspected situations (e.g. new value was added to enum).
What about code like presented in snippet 2? In my opinion cannot be worst. If we deal with exception this way it is better to remove try-catch-finally statements and live code without this line. Why? Code will be much simpler (less line of code) and will behave in way we understand.
There is no worst situation when we start writing code which we don't understand.
According to code presented in snippet 3 in my opinion we should alway perform validation of parameters and execute code if passed parameters are valid. Why? The answer is simple we can add additional message why exception was thrown, and which parameters we excepted.
And finally ... we should remember about document all exception which we trow from methods and why using xml documentation feature. Because of that client knows which exceptions have to be caught.
I will have forgotten about one important thing: which and why class we should use as base class for our exceptions. According to books written by Jeffrey Richter we should use Exception class instead of ApplicationException. In many guidance we can read that we should use ApplicationException class as base class for our exceptions because CLR use SystemException class as their base class. But there is one case in CLR where this rule is broken (CLR class is deriving from ApplicationException - sorry I don't remember which one), so there is no need to increase inheritance tree of exceptions.

Anonymous said...

Useful tips on exception handling! I may add a few:
* Return null for extremely common error cases. For example, File.Open returns null if the file is not found, but throws an exception if the file is locked.
* Throw an InvalidOperationException if a property set or method call is not appropriate given the object's current state.
* When creating user-defined exceptions, end exception class names with the word "Exception", for example DataAccessException.
* Ensure that your user-defined exception class is accessible including when exceptions occur across application domains. If exception class is not found, CLR throws FileNotFoundException.

Wibbo said...

The reason for not always validating parameters is related to performance. Microsoft actually introduced the TryParse mechanism because of the high failure rate of using Parse with invalid parameters.

I'm not convinced by documenting every exception that a method can throw. Microsoft decided against checked exceptions, rightly or wrongly, for a number of reasons. Let's not start that debate again here :-) Besides, if you document that a method throws a particular exception then what happens to derived exceptions? We have to be careful not to introduce additional maintenance unless there are cast-iron benefits.

Unfortunately, Microsoft's original intention that all CLR exceptions should derive from SystemException and application exceptions should derive from ApplicationException no longer stands.
Many exceptions are derived directly from System.Exception and some exceptions have the wrong base type as you hinted (e.g. FormatException and TargetInvocationException).

Anonymous said...

nice art :)