CAUBLESTONE INK

.net development and other geeky stuff

Exception Handling Basics

Posted on April 14th, 2009


Download the code Here.

It’s amazing to see how exception handling is not used properly after all these years that the .net framework has been out. Some of it can be attributed to the Internet since anybody can post information anywhere, it get’s propagated through google and then upcoming developers find it and think that is how things are done. Of course for those that understand exception handling it’s always obvious when you see a rookie mistake. I don’t think we should always blame the rookie since still to this day people post things with bad exception handling just making the problem worse.

The goal of this is to show you a very very simple sample to show how you should code your exception handling so that if you ever have to debug a problem you can get right to it instead of having to find it.

I’m going to give you three examples of exception handling. Two of them work correctly. The first one I will show is what you tend to find a lot. This is the one that is wrong. I cannot say that rookies are the only ones that do this either. There are plenty of people that are very strong that still make this simple mistake. However it can mean a world of difference when done right and you run into a production problem.

The wrong way:

public void ExceptionHandlingA()
{
    try
    {
	throw new ArgumentNullException("ExceptionA", "Test Exception Call from DoStuff.ExceptionHandlingA");
    }
    catch (Exception ex)
    {
	throw ex;
    }
}

Now you may ask or even tell yourself that there is nothing wrong with this statement. The problem is both subtle and devious. It all hinges on your Catch block. Just looking at this statement you think it bubbles the exception up. While it does bubble it up what is wrong with this is that it bubbles it up as a new exception object. While if you only have a single layer of code this would never be obvious. However if you are calling this method from other classes that then are called by other classes is where you start to run into your problem.

Next I’ll show you two proper methods that do exception handling and then walk you through creating the code you need to show the very subtle difference in the result. Namely the stacktrace.

Good Exceptions:

public void ExceptionHandlingB()
{
    try
    {
	throw new ArgumentNullException("ExceptionB", "Test Exception Call from DoStuff.ExceptionHandlingB");
    }
    catch (Exception ex)
    {
	throw;
    }
}

public void ExceptionHandlingC()
{
    try
    {
	throw new ArgumentNullException("ExceptionC", "Test Exception Call from DoStuff.ExceptionHandlingC");
    }
    catch
    {
	throw;
    }
}

Now if you look at this code you will see that the only thing that is different is that in the Catch block we only use the keyword throw. This is very important and something you should understand. Basically when you call just the word throw it does a re-throw in the IL for .net whereas the other throw ex command creates a new exception.

In case you don’t believe what I’ve just said here is the IL from the Bad exception handler:

.method public hidebysig instance void  ExceptionHandlingA() cil managed
{
  // Code size       22 (0x16)
  .maxstack  3
  .locals init ([0] class [mscorlib]System.Exception ex)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "ExceptionA"
    IL_0007:  ldstr      "Test Exception Call from DoStuff.ExceptionHandlingA"
    IL_000c:  newobj     instance void [mscorlib]System.ArgumentNullException::.ctor(string,
                                                                                     string)
    IL_0011:  throw
  }  // end .try
  catch [mscorlib]System.Exception
  {
    IL_0012:  stloc.0
    IL_0013:  nop
    IL_0014:  ldloc.0
    IL_0015:  throw
  }  // end handler
} // end of method DoStuff::ExceptionHandlingA

and the code from the good exception handler:

.method public hidebysig instance void  ExceptionHandlingC() cil managed
{
  // Code size       22 (0x16)
  .maxstack  3
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "ExceptionC"
    IL_0007:  ldstr      "Test Exception Call from DoStuff.ExceptionHandlingC"
    IL_000c:  newobj     instance void [mscorlib]System.ArgumentNullException::.ctor(string,
                                                                                     string)
    IL_0011:  throw
  }  // end .try
  catch [mscorlib]System.Object
  {
    IL_0012:  pop
    IL_0013:  nop
    IL_0014:  rethrow
  }  // end handler
} // end of method DoStuff::ExceptionHandlingC

If you notice towards the end of the IL that on the good one it uses the command rethrow where the bad handler does not. This is very important as the rethrow command allows your exception objects to retain the full stacktrace. Since stacktraces are very important in helping you find where your problem is the better it is the faster you can find your problem and fix it.

Now to show you what we have done so you can see for your own eyes. Create a new VS Console project or just put this stuff in notepad and compile using the SDK. Whatever your preference. I’ve included a VS2008 version of the project for download if you just want to download and run it.

Take the first three methods from a above and put them in a class called DoStuff like so:

using System;
using System.Collections.Generic;
using System.Text;

namespace EffectiveErrorHandling
{
    class DoStuff
    {
        public void ExceptionHandlingA()
        {
            try
            {
                throw new ArgumentNullException("ExceptionA", "Test Exception Call from DoStuff.ExceptionHandlingA");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void ExceptionHandlingB()
        {
            try
            {
                throw new ArgumentNullException("ExceptionB", "Test Exception Call from DoStuff.ExceptionHandlingB");
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public void ExceptionHandlingC()
        {
            try
            {
                throw new ArgumentNullException("ExceptionC", "Test Exception Call from DoStuff.ExceptionHandlingC");
            }
            catch
            {
                throw;
            }
        }

    }
}

Next we are going to create a second layer that calls this class using the same exception handling constructs for each method type so we create a layer that can absorb our exceptions to show the loss of the stacktrace.


using System;
using System.Collections.Generic;
using System.Text;

namespace EffectiveErrorHandling
{
    class CallStuff
    {
        public void CallExceptionA()
        {
            try
            {
                DoStuff d = new DoStuff();
                d.ExceptionHandlingA();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void CallExceptionB()
        {
            try
            {
                DoStuff d = new DoStuff();
                d.ExceptionHandlingB();
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public void CallExceptionC()
        {
            try
            {
                DoStuff d = new DoStuff();
                d.ExceptionHandlingC();
            }
            catch
            {
                throw;
            }
        }

    }
}

Next put the following code in your Program.cs file:

using System;
using System.Collections.Generic;
using System.Text;

namespace EffectiveErrorHandling
{
    class Program
    {
        static void Main(string[] args)
        {
            CallStuff c = new CallStuff();

            Console.WriteLine("Calling Exception Type A");
            try
            {
                c.CallExceptionA();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
            }
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Calling Exception Type B");
            try
            {
                c.CallExceptionB();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
            }
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Calling Exception Type C");
            try
            {
                c.CallExceptionC();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
            }
        }
    }
}

As you can see here we are handling the exception that is raised in each case and outputing it to the console.

Download the code Here.

Now if you are trusting this is the output from the program when you run it. As you can see the first exception has one less level of detail due to the fact that it does not rethrow the exception but instead it creates a new copy of the exception for bubbling up. So as you are progressing through your development career please take this to heart. You’ll thank me for it when you run up against some bug which you could have found and fixed if you had the right exception handling in place.

Output from the console:


Calling Exception Type A
Test Exception Call from DoStuff.ExceptionHandlingA
Parameter name: ExceptionA
at EffectiveErrorHandling.CallStuff.CallExceptionA() in G:\a951072\My Documen
ts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Cal
lStuff.cs:line 18
at EffectiveErrorHandling.Program.Main(String[] args) in G:\a951072\My Docume
nts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Pr
ogram.cs:line 16

Calling Exception Type B
Test Exception Call from DoStuff.ExceptionHandlingB
Parameter name: ExceptionB
at EffectiveErrorHandling.DoStuff.ExceptionHandlingB() in G:\a951072\My Docum
ents\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\D
oStuff.cs:line 29
at EffectiveErrorHandling.CallStuff.CallExceptionB() in G:\a951072\My Documen
ts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Cal
lStuff.cs:line 31
at EffectiveErrorHandling.Program.Main(String[] args) in G:\a951072\My Docume
nts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Pr
ogram.cs:line 27

Calling Exception Type C
Test Exception Call from DoStuff.ExceptionHandlingC
Parameter name: ExceptionC
at EffectiveErrorHandling.DoStuff.ExceptionHandlingC() in G:\a951072\My Docum
ents\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\D
oStuff.cs:line 42
at EffectiveErrorHandling.CallStuff.CallExceptionC() in G:\a951072\My Documen
ts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Cal
lStuff.cs:line 45
at EffectiveErrorHandling.Program.Main(String[] args) in G:\a951072\My Docume
nts\Visual Studio 2008\Projects\EffectiveErrorHandling\EffectiveErrorHandling\Pr
ogram.cs:line 38