Sponsored Links

Exceptions and Exception Handling

Things you should know

There's a couple of things all .NET programmers should be aware of when dealing with exceptions:

  1. Exceptions are expensive!: Throwing an exception interrupts the flow of control and forces the .NET runtime to navigate the call stack in order to handle the exception. For that reason you should always check for error conditions explicitly and handle them at the start of the function rather than allow them to fall through and get caught later on (this is good practice anyway as it prevents wasted execution time).
  2. Exceptions are objects too: You can store exceptions as objects and pass references to them around like you can any other object - this may help in handling and logging them depending on your setup.
  3. There's a difference between "throw;" and "throw ex;": Catching an exception you can't handle should be avoided (instead, let the exception bubble up to the Global exception handler), but if you do need to rethrow an exception you should be aware of exactly what happens when you rethrow it. If you rethrow the exception using the throw; statement then nothing is done to the exception - its stack trace is preserved and it is simply bubbled up to the next handler. If, however, you rethrow using the "throw ex;", where ex is the exception in question, then the stack trace of the exception is rewritten as that of the point where it was rethrown, thus wiping out valuable debug information about where the exception originated).

The Base Exception Class

The base exception class contains some very useful properties. The first of these is the "Message" property, that should contain a useful string description of the error to let a technical user know what has gone wrong (this is not a user friendly error message). The second is the StackTrace, which is a string containing a list (stack) of the functions that were in the process of execution at the point the error occured. This will probably be your first port of call when debugging a problem. The final property that will probably be useful to you is the InnerException property - if you can't find any specific details for the error in the main exception, check the InnerException (and possibly the InnerException's InnerException) for further information.

Handling Exceptions

You should hopefully already be aware of the basic exception handling "try {} catch {} finally {}" structure. The try block is the piece of code in which you are allowing for exceptions to occur. Following the try block you can specify any number of catch blocks to catch specific types of exception (using the catch (XXXException ex){} notation). When declaring these blocks you should order them with the most specific types of exception first and the more general ones later. If you want to handle multiple types exceptions in the same way from a single try block then you should declare a function to handle the exception and pass the exception on to that function as a parameter since there is no way of sharing exception handler blocks between different exception types.

The finally block is used to clear up resources after the possibility of an exception occuring. In .NET 2 Microsoft introduced the "using" structure, which can help reduce the need for finally blocks and generally keeps code more clean. The Using block takes an object declaration as a parameter (e.g "using (Filestream f = new Filestream("C:/test.txt", FileAccess.Write) { ... }" ) and calls the dispose method of that object once you leave that block (even if you leave it by raising an exception). This makes it a very good way of ensuring that all connections, files and streams are closed once you are done with them and have a defined period of scope. Don't get this notation confused with the "using" that is applied to add namespaces at the beginning of the class - they are two different commands.

Exceptions are "bubbled" up the call stack. That means, if there is no catch block that catches the exception or one of its parents then the exception will be passed up the call stack until an exception handler is found.

Prior to .NET 2 it used to be that COM exceptions could only be caught using the "catch { }" block ("catch (Exception ex) {} didn't work"). This has been changed in .NET 2 since it was causing and COM exceptions are now wrapped in their own type inheritting from the System.Exception class.

Throwing Exceptions

If you are throwing your own exceptions it is best practice to define your own exception appropriate to the issue that is causing the problem. I have seen this practice overlooked in industry many times in favour of using "ApplicationException" as a placeholder. This should only ever be done if the exception will be caught directly from the try block and even then is still not best practice.

You should also be aware that exceptions should only ever be thrown from the namespace that declared them unless they are declared in the System namespace, so you should not go through the .NET Framework looking for appropriate exceptions to throw unless they live in System. If no exceptions are appropriate from the System namespace, you should declare your own exception heirarchy appropriate to the namespace you are operating in. If you are defining your own exceptions remember that you should implement an appropriate chain of inheritance since this is vital for accurate exception handling. You declare inheritance on an exception the same way you would on any other class (e.g public MyChildException : MyParentException {} ).

Once you have an exception type that is appropriate all you need to do is call the constructor and then call the "throw ex;" where ex is the exception you want to throw.