Exception Handling in C#

Introduction

Exceptions are unforeseen errors that happen in your programs. Most of the time, you can, and should, detect and handle program errors in your code. For example, validating user input, checking for null objects, and verifying the values returned from methods are what you expect, are all examples of good standard error handling that you should be doing all the time.

However, there are times when you don't know if an error will occur. For example, you can't predict when you'll receive a file I/O error, run out of system memory, or encounter a database error. These things are generally unlikely, but they could still happen and you want to be able to deal with them when they do occur. This is where exception handling comes in. When exceptions occur, they are said to be "thrown". What is actually thrown is an object that is derived from the System.Exception class. The System.Exception class provides several methods and properties for obtaining information on what went wrong. Identifying the exceptions you will need to handle depends on the routine you are writing.

There are two types of exceptions: exceptions generated by an executing program and exceptions generated by the common language runtime. System.Exception is the base class for all exceptions in C#. Several exception classes inherit from this class including ApplicationException and SystemException. These two classes form the basis for most other runtime exceptions. Other exceptions that derive directly from System.Exception include IOException, WebException etc.

The common language runtime throws SystemException. The ApplicationException is thrown by a user program rather than the runtime. It is not recommended that we catch SystemExceptions nor is it good programming practice to throw SystemExceptions in our applications.

System.OutOfMemoryException

System.NullReferenceException

System.InvalidCastException

System.ArrayTypeMismatchException

System.IndexOutOfRangeException

System.ArithmeticException

System.DivideByZeroException

System.OverFlowException

Try/catch Block

When exceptions are thrown, you need to be able to handle them. This is done by implementing a try/catch block. Code that could throw an exception is put in the try block an exception handling code goes in the catch block. The programs in this lesson cause exceptions on purpose. The exception that you see is generated intentionally to show you what the exception message looks like before you see it yourself in your own programs.

Although the code has a single catch block, all exceptions will be caught there because the type is of the base exception type "Exception". In exception handling, more specific exceptions will be caught before their more general parent exceptions. For example, the following code shows how to place multiple catch blocks:

If the file doesn't exist, a FileNotFoundException exception will be thrown and caught by the first catch block. However, if a PathTooLongException exception was raised, the second catch part would catch the exception. This is because there isn't a catch block for the PathTooLongException exception and the generic Exception type catch block is the only option available to catch the exception.

Exceptions that are not handled will normally come up the stack until a calling routine in the call chain handles them. If you forget to include try/catch blocks in a part of your code and there aren't any try/catch blocks earlier in the call chain, your program will abort with a message describing the exception. It is good practice to provide exception handling in your programs.

Finally Block

An exception can leave your program in an inconsistent state by not releasing resources or doing some other type of cleanup. A catch block is a good place to figure out what may have went wrong and tries to recover, however it can't account for all scenarios. Sometimes you need to perform clean up actions whether or not your program succeeds.

A file stream must be closed when you’re done with it. In this example below the file stream is the resource that needs to be cleaned up. outStream is opened successfully, meaning the program now has a handle to an open file resource.

When trying to open the inStream, a FileNotFoundException exception is raised, causing control to go immediately to the catch block. It's possible to close the outStream in the catch block, but what if the algorithm executed successfully without an exception? On success, the file would never be closed.I have included a finally block which will always be executed, regardless of whether the algorithm in the try block raises an exception or not, the code in the finally block will be executed before control leaves the method.

A finally block is not required and you may ask what happens if you just put code after the catch block, under normal circumstances, if the exception is caught, all code following the catch will be executed. However, try/catch/finally is for exceptional circumstances and it is better to plan for the worst to make your program more robust. For example, if one of the catch handlers throws an exception again or caused another exception, the code following the catch block would never be executed. Also, if you don't catch the exception at all, program flow would immediately do a stack walk looking for an exception handler that fits and the code following the catch blocks would not be executed. Since there is too much potential for code in an algorithm to not be executed, a finally block is your insurance for executing those critical actions you need.