This is the second part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

Today we are going to dive into exceptions in .NET world. Handling them right might be a bit tricky, especially when you do not understand internals.

Exceptions at a glance

I assume you know what is an exception, when it should be used, how to throw it, and how to catch it. You can also read about exception internals

Rethrowing exceptions

We know how to throw and catch exception. Imagine now, that we want to catch exception, examine it and do something (e.g., log its message), and then rethrow it. Let’s take the following code:

01. using System;
02. using System.Threading;
03. 
04. namespace ExceptionTests
05. {
06.     class Program
07.     {
08.         public static void MethodThrowingException()
09.         {
10.             throw new ArgumentException("Hold on!");
11.         }
12. 
13.         public static void MethodRethrowingException()
14.         {
15.             try
16.             {
17.                 MethodThrowingException();
18.                 MethodThrowingException();
19.             }
20.             catch (Exception e)
21.             {
22.                 if (e.Message == "Custom message")
23.                 {
24.                     Console.WriteLine("Custom exception");
25.                 }
26. 
27.                 // Rethrow it here
28.             }
29.         }
30. 
31.         static void Main()
32.         {
33.             Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
34.             MethodRethrowingException();
35.         }
36.     }
37. }

We have method named MethodThrowingException which throws exception of type ArgumentException. Next, we have method MethodRethrowingException which calls MethodThrowingException twice, handles exception and rethrows it. Finally, in Main we first set culture to English (in order to have readable exception descriptions), and next we call method handling exceptions.

Two things are worth noting here: we call method MethodThrowingException twice (even though it always throws exception and second invocation never happens). We also check the message of the exception when handling it, but it never meets condition so we always rethrow it without doing anything.

Since Main does not handle exceptions, they will be printed out to the console (with all inner exceptions), and the program will crash. Let’s now examine three methods of rethrowing exception.

Rethrowing by throwing the same exception again

We can use the following line:

throw e;

It throws existing exception. Let’s see it in action, this is the program output:

Unhandled Exception: System.Exception: Hold on!
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 27
   at ExceptionTests.Program.Main() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 34

So what did we get here? Our stacktrace contains only information related to last throw. We lost all informations about the exception being thrown in the first place.

Never rethrow exception this way!

Let’s now see the stacktrace visible by the debugger:

0:000> !clrstack
OS Thread Id: 0x4bac (0)
Child SP       IP Call Site
00f3ee4c 755bdae8 [HelperMethodFrame: 00f3ee4c]
00f3eefc 01580537 *** WARNING: Unable to verify checksum for ConsoleApplication2.exe
ExceptionTests.Program.MethodRethrowingException() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 29]
00f3eff0 0158049c ExceptionTests.Program.Main() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 34]
00f3f170 70131376 [GCFrame: 00f3f170]

We can’t see original frame of the method which was first to throw.

Rethrowing by throw;

Most of the C# books says that the right way to rethrow exception is the instruction throw;. Let’s see it in action:

Unhandled Exception: System.ArgumentException: Hold on!
   at ExceptionTests.Program.MethodThrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 10
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 27
   at ExceptionTests.Program.Main() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 34

Indeed, this time we have line which originally thrown the exception. Let’s see WinDBG stacktrace:

0:000> !clrstack
OS Thread Id: 0xff0 (0)
Child SP       IP Call Site
00b7f11c 755bdae8 [HelperMethodFrame: 00b7f11c]
00b7f18c 02910534 *** WARNING: Unable to verify checksum for ConsoleApplication2.exe
ExceptionTests.Program.MethodRethrowingException() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 29]
00b7f280 0291049c ExceptionTests.Program.Main() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 34]
00b7f3f8 70131376 [GCFrame: 00b7f3f8]

Unfortunately, stacktrace didn’t change. We still can’t see the datils. But we have them in our exception object, so we can extract them anyway, can’t we?

Well, the answer is: not really. Are you able to tell which invocation of MethodThrowingException thrown exception? Remember that we are calling this method twice but there is no clue in stacktrace or exception which invocation was the one to blame. We could try to analyze the code but we have no stack parameters anymore, so it is not always sufficient.

Most books describe throw; as a proper way to rethrow exception. But it is not enough!

So how should we rethrow exception?

Create new exception and throw it

Let’s now rethrow the exception with the following line:

throw new Exception(e.Message, e)

Output:

Unhandled Exception: System.Exception: Hold on! ---> System.ArgumentException: Hold on!
   at ExceptionTests.Program.MethodThrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 10
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 17
   --- End of inner exception stack trace ---
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 27
   at ExceptionTests.Program.Main() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 34

This time we finally have all informations. Let’s see the WinDBG stacktrace:

0:000> !clrstack
OS Thread Id: 0x470 (0)
Child SP       IP Call Site
004ff144 755bdae8 [HelperMethodFrame: 004ff144]
004ff1f4 00760560 *** WARNING: Unable to verify checksum for ConsoleApplication2.exe
ExceptionTests.Program.MethodRethrowingException() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 29]
004ff2f0 0076049c ExceptionTests.Program.Main() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 34]
004ff468 70131376 [GCFrame: 004ff468]

Unfortunately, stacktrace is still the same.

But! This changes the type of the exception which can change the logc of your code badly. SO you should not do it just like that, you should probably use reflection to create exception of the same type (which might no be possible though).

ExceptionDispatchInfo

.NET 4.5 introduced class ExceptionDispatchInfo which can capture exception and rethrow it somewhere else without spoiling the stack trace. Let’s see it in action:

ExceptionDispatchInfo.Capture(e).Throw();

Output:

Unhandled Exception: System.Exception: Hold on!
   at ExceptionTests.Program.MethodThrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 10
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 17
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 27
   at ExceptionTests.Program.Main() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 34

And now WinDBG callstack:

0:000> !clrstack
OS Thread Id: 0x470 (0)
Child SP       IP Call Site
004ff144 755bdae8 [HelperMethodFrame: 004ff144]
010ff3e8 016d0544 *** WARNING: Unable to verify checksum for ConsoleApplication2.exe
ExceptionTests.Program.MethodRethrowingException() [C:\Users\user\Documents\Visual Studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 27]
010ff4e0 016d049c ExceptionTests.Program.Main() [C:\Users\user\Documents\Visual Studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 34]

It looks like nothing changed.

So what is the right way to rethrow exception? It looks like creating new exception is the best. It works on old .NET Framework and contains most of useful data. However, it changes the type of the exception so you probably should not do it directly but use some reflection mechanism where possible. If in .NET 4.5, just use new method.

Examining exception

Our exception handler does something interesting only when the message is set to correct value. Most of the times this is not the case so it does nothing. Is there a better approach to perform this message check?

C# 6 introduced filtering statement for exception handlers. Let’s see them in action:

01. using System;
02. using System.Threading;
03. 
04. namespace ExceptionTests
05. {
06.     class Program
07.     {
08.         public static void MethodThrowingException()
09.         {
10.             throw new Exception("Hold on!");
11.         }
12. 
13.         public static void MethodRethrowingException()
14.         {
15.             try
16.             {
17.                 MethodThrowingException();
18.                 MethodThrowingException();
19.             }
20.             catch (Exception e) when (e.Message == "Custom message")
21.             {
22.                 Console.WriteLine("Custom exception");
23. 
24.                 // Rethrow
25.             }
26.         }
27. 
28.         static void Main()
29.         {
30.             Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
31.             MethodRethrowingException();
32.         }
33.     }
34. }

So we moved our condition to special filter outside of the exception handler. Does it change anything? If you run this application with different rethrowing instructions, the output will be different:

Unhandled Exception: System.Exception: Hold on!
   at ExceptionTests.Program.MethodThrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 10
   at ExceptionTests.Program.MethodRethrowingException() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 17
   at ExceptionTests.Program.Main() in c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 31

There is no surprise here: since our exception handler didn’t execute, we didn’t need to rethrow exception, so we get original stacktrace regardless the rethrowing method. Let’s now see the WinDBG:

0:000> !clrstack
OS Thread Id: 0x5d18 (0)
Child SP       IP Call Site
008ff3cc 755bdae8 [HelperMethodFrame: 008ff3cc] 
008ff47c 00ab0607 *** WARNING: Unable to verify checksum for ConsoleApplication2.exe
ExceptionTests.Program.MethodThrowingException() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 9]
008ff48c 00ab04ed ExceptionTests.Program.MethodRethrowingException() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 17]
008ff4d0 00ab049c ExceptionTests.Program.Main() [c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs @ 31]
008ff648 70131376 [GCFrame: 008ff648]

This is very important. New syntax allows us to not execute the exception handler when the condition is not met. When it is the case, the stacktrace does not change!

Conclusion

Most of C# books describes throw; instruction as the way to go. However, be very careful with it since it loses some pieces of information. It might look like not interesting edge case (why do you want to call particular function multiple times?), but you should be aware of this behaviour. Also, always try to use new syntax for filtering exceptions since it doesn’t modify call stack. One day it might save your life.