Exception handling

In every program, things go wrong sometimes. With C#, we're blessed with a good compiler, which will help us prevent some of the most common mistakes. Obviously it can't see every error that might happen, and in those cases, the .NET framework will throw an exception, to tell us that something went wrong. In an earlier chapter, about arrays, I described how we would get an exception if we tried to stuff too many items into an array. Let's bring the example:
using System;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = new int[2];

            numbers[0] = 23;
            numbers[1] = 32;
            numbers[2] = 42;

            foreach(int i in numbers)
                Console.WriteLine(i);
            Console.ReadLine();
        }
    }
}
Okay, try running this example, and you will see what I'm talking about. Do you see what we're doing wrong? We have defined an array of integers with room for 2 items, yet we try to use 3 spaces in it. Obviously, this leads to an error, which you will see if you try to run this example. When run inside Visual C# Express, the IDE gives us some options for the exception, but if you try to execute the program by simply doubleclicking the EXE file, you will get a nasty error. If you know that an error might occur, you should handle it. This is where exceptions are used. Here is a slightly modified version of the code from above:
int[] numbers = new int[2];
try
{
    numbers[0] = 23;
    numbers[1] = 32;
    numbers[2] = 42;

    foreach(int i in numbers)
        Console.WriteLine(i);
}
catch
{
    Console.WriteLine("Something went wrong!");
}
Console.ReadLine();
Let me introduce to you your new best friend when it comes to error handling: the try..catch block. Try running the program now, and see the difference - instead of Visual C# Express/Windows telling us that a serious problem occured, we get to tell our own story. But wouldn't it be nice if we could tell what went wrong? No problem:
catch(Exception ex)
{
    Console.WriteLine("An error occured: " + ex.Message);
}
As you can see, we have added something to the catch statement. We now tell which exception we want caught, in this case the base of all exceptions, the Exception. By doing so, we get some information about the problem which caused the exception, and by outputting the Message property, we get an understandable description of the problem.

As I said, Exception is the most general type of exception. The rules of exception handling tells us that we should always use the least general type of exception, and in this case, we actually know the exact type of exception generated by our code. How? Because Visual Studio told us when we didn't handle it. If you're in doubt, the documentation usually describes which exception(s) a method may throw. Another way of finding out is using the Exception class to tell us - change the output line to this:
Console.WriteLine("An error occured: " + ex.GetType().ToString());
The result is, as expected, IndexOutOfRangeException. We should therefore handle this exception, but nothing prevents us from handling more than one exception. In some situations you might wish to do different things, depending on which exception was thrown. Simply change our catch block to the following:
catch(IndexOutOfRangeException ex)
{
    Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
    Console.WriteLine("Some sort of error occured: " + ex.Message);
}
As you can see, we look for the IndexOutOfRangeException first. If we did it the other way around, the catch block with the Exception class would get it, because all exceptions derive from it. So in other words, you should use the most specific exceptions first.

One more thing you should know about concerning exceptions is the finally block. The finally block can be added to a set of catch blocks, or be used exclusively, depending on your needs. The code within the finally block is always run - exception or no exception. It's a good place if you need to close file references or dispose objects you won't need anymore. Since our examples have been pretty simple so far, we haven't really been in need of any cleanup, since the garbage collector handles that. But since will likely run into situations where you need the finally block, here is an extended version of our example:
int[] numbers = new int[2];
try
{
    numbers[0] = 23;
    numbers[1] = 32;
    numbers[2] = 42;

    foreach(int i in numbers)
        Console.WriteLine(i);
}
catch(IndexOutOfRangeException ex)
{
    Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
    Console.WriteLine("Some sort of error occured: " + ex.Message);
}
finally
{
    Console.WriteLine("It's the end of our try block. Time to clean up!");
}
Console.ReadLine();
If you run the code, you will see that both the first exception block and the finally block is executed. If you remove the line that adds the number 42 to the array, you will see that only the finally block is reached.

Another important part you should know about exceptions, is how they impact the method in which the exceptions occur. Not all unhandled exceptions are fatal for your application, but when they aren't, you should not expect the remaining code of the method to be executed. On the other hand, if you do handle the exception, only the lines after the try block will be executed. In our example, the loop that outputs the values of the array is never reached, because the try block goes straight to the catch/finally block(s) once an exception is thrown. However, the last line, where we read from the console to prevent the application from exiting immediately, is reached. You should always have this in mind when you construct try blocks.
<PreviousNext>
^ Top