Software Development

Throwing exceptions

When reviewing some code today I noticed some code that catches an exception, does something with it and then explicitly throws it again. The code looked something like this:

try
{
    // Do something that might cause an exception
}
catch (Exception ex)
{
    // Some stuff
    throw ex;
}

The problem with the above code is that when you throw the exception again the details about where the exception originated from are lost because the throw populates that part of the exception object. So the original details are replaced with the details about the current location in the code.

Consider the following program:

static void Main(string[] args)
{
    try
    {
        A();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.StackTrace);
    }
    Console.ReadLine();
}

private static void A()
{
    B();
}

private static void B()
{
    try
    {
        C();
    }
    catch (Exception ex)
    {
        // I can do something
        Console.WriteLine("Method C() catches the exception and partly handles it");
        Console.WriteLine();
        throw ex;
    }
}

private static void C()
{
    D();
}

private static void D()
{
    Exception ex = new Exception("This exception is thrown in D");
    throw ex;
}

The original exception is thrown by method D. The code in method B catches the exception and partly handles it, it then explicitly throws the original exception again. When the exception is finally caught in the Main method the call stack is truncated to method B. It can no longer see that method C and D were also called.

The output of the application is:

Method C() catches the exception and partly handles it

This exception is thrown in D
   at ConsoleApplication1.Program.B() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 40
   at ConsoleApplication1.Program.A() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 26
   at ConsoleApplication1.Program.Main(String[] args) in d:DevelopmentConsoleA
pplication1ConsoleApplication1Program.cs:line 14

As you can see, the stack trace only goes from the point of the second throw to the point that the exception is caught.

There are two correct solutions to this problem.

Solution 1: Wrapping the Exception

If you have additional information to add to the exception object you can create a brand new Exception and then put the original exception in as an inner exception like this:

try
{
    // Do something that might cause an exception
}
catch(Exception ex)
{
    // Some stuff
    Exception moreDetailedEx = new Exception("A message with more details", ex);
    throw moreDetailedEx;
}

NOTE: Please use a specific exception class and not Exception this makes catching specific types of exception easier and more efficient as the compiler can put in place some optimisations for you over you catching the base Exception class and examining it. Iā€™m using Exception here simply to make the example easier to read.

So, if we change our program above to create a new exception and wrap the old one in it it will now look like this:

static void Main(string[] args)
{
    try
    {
        A();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.InnerException.Message);
        Console.WriteLine(ex.InnerException.StackTrace);
        Console.WriteLine();
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.StackTrace);
    }
    Console.ReadLine();
}

private static void A()
{
    B();
}

private static void B()
{
    try
    {
        C();
    }
    catch (Exception ex)
    {
        // I can do something
        Console.WriteLine("Method C() catches the exception and partly handles it");
        Console.WriteLine();
        Exception newEx = new Exception("This exception is thrown in B", ex);
        throw newEx;
    }
}

private static void C()
{
    D();
}

private static void D()
{
    Exception ex = new Exception("This exception is thrown in D");
    throw ex;
}

I’ve added some extra bits to the Main method to show the InnerException details too. The output of the program now looks like this:

Method C() catches the exception and partly handles it

This exception is thrown in D
   at ConsoleApplication1.Program.D() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 56
   at ConsoleApplication1.Program.C() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 50
   at ConsoleApplication1.Program.B() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 36

This exception is thrown in B
   at ConsoleApplication1.Program.B() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 44
   at ConsoleApplication1.Program.A() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 29
   at ConsoleApplication1.Program.Main(String[] args) in d:DevelopmentConsoleA
pplication1ConsoleApplication1Program.cs:line 14

As you can now see all the information is available. It can now be seen the patch from the final point the exception was caught to the point it was originally thrown.

Solution 2: Re-throwing the Exception

If you do not have any additional information to add to the exception you can simply use the throw keyword on its own and it will keep the existing exception object without altering it. For example:

try
{
    // Do something that might cause an exception
}
catch (Exception ex)
{
    // Some stuff
    throw;
}

Changing our program above to use the throw statement on its own will mean the program now looks like this:

static void Main(string[] args)
{
    try
    {
        A();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.StackTrace);
    }
    Console.ReadLine();
}

private static void A()
{
    B();
}

private static void B()
{
    try
    {
        C();
    }
    catch (Exception ex)
    {
        // I can do something
        Console.WriteLine("Method C() catches the exception and partly handles it");
        Console.WriteLine();
        throw;
    }
}

private static void C()
{
    D();
}

private static void D()
{
    Exception ex = new Exception("This exception is thrown in D");
    throw ex;
}

And the output now looks like this:

Method C() catches the exception and partly handles it

This exception is thrown in D
   at ConsoleApplication1.Program.D() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 52
   at ConsoleApplication1.Program.C() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 46
   at ConsoleApplication1.Program.B() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 40
   at ConsoleApplication1.Program.A() in d:DevelopmentConsoleApplication1Cons
oleApplication1Program.cs:line 26
   at ConsoleApplication1.Program.Main(String[] args) in d:DevelopmentConsoleA
pplication1ConsoleApplication1Program.cs:line 14

As you can see the stack trace now shows you the entire route between the point the exception was caught and when it was thrown. We can also see from the message that the method C still caught and partly handled the exception.

Using the throw keyword like this actually translates to the CIL (MSIL) rethrow command. When you use throw with an Exception object it translates to the CIL throw command.

When the exception is thrown in D the CIL looks like this:

.method private hidebysig static void D() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [mscorlib]System.Exception ex)
    L_0000: nop
    L_0001: ldstr "This exception is thrown in D"
    L_0006: newobj instance void [mscorlib]System.Exception::.ctor(string)
    L_000b: stloc.0
    L_000c: ldloc.0
    L_000d: throw
}

The key above is L_000d where it throws the exception.

Compare that to method B:

.method private hidebysig static void B() cil managed
{
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.Exception ex)
    L_0000: nop
    L_0001: nop
    L_0002: call void ConsoleApplication1.Program::C()
    L_0007: nop
    L_0008: nop
    L_0009: leave.s L_0020
    L_000b: stloc.0
    L_000c: nop
    L_000d: ldstr "Method C() catches the exception and partly handles it"
    L_0012: call void [mscorlib]System.Console::WriteLine(string)
    L_0017: nop
    L_0018: call void [mscorlib]System.Console::WriteLine()
    L_001d: nop
    L_001e: rethrow
    L_0020: nop
    L_0021: ret
    .try L_0001 to L_000b catch [mscorlib]System.Exception handler L_000b to L_0020
}

In the above CIL code the key is L_001e where it rethrows the exception. This is where the CIL is much more explicit than C#. In C# the throw keyword is overloaded and functions differently depending on whether it receives an exception object or not.

2 thoughts on “Throwing exceptions

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s