Throw vs Throw ex

This tidbit should be commonly known but in my experience is often overlooked and therefore worth a blog post. In short, there is a big difference between using Throw and Throw ex to re-throw an exception in .NET. Essentially, when using the Throw ex syntax parameters including the stack trace itself are reset resulting in unreliable stack traces when exceptions are bubbled up through an application.

To provide a brief example take a look at the following code. I have created a console application with three classes:

image

We have Program – the entry point to the console application. The main procedure creates an instance of a DogWalkerClass and runs the WalkDogWithThrowEx or WalkDogWithThrow procedure depending on the value of the throwWithEx field. Both of these procedures in turn create an instance of the BadDog class and calls the WalkDog procedure. The WalkDog procedure has one-and-only-one purpose – to throw an exception.

Program.cs

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: 
  6: namespace ThrowDemo
  7: {
  8:     class Program
  9:     {
 10:         static void Main(string[] args)
 11:         {
 12:             bool throwWithEx = true;
 13:             DogWalkerClass dw = new DogWalkerClass();
 14: 
 15:             try
 16:             {
 17:                 if (throwWithEx)
 18:                 {
 19:                     dw.WalkDogWithThrowEx();
 20:                 }
 21:                 else
 22:                 {
 23:                     dw.WalkDogWithThrow();
 24:                 }
 25:             }
 26:             catch (Exception ex)
 27:             {
 28:                 Console.WriteLine(ex.StackTrace);
 29:                 Console.ReadLine();
 30:             }
 31:         }
 32:     }
 33: }


 



DogWalkerClass.cs



  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: 
  6: namespace ThrowDemo
  7: {
  8:     class DogWalkerClass
  9:     {
 10:         public void WalkDogWithThrow()
 11:         {
 12:             try
 13:             {
 14:                 BadDog gd = new BadDog();
 15:                 gd.WalkDog();
 16:             }
 17:             catch (Exception ex)
 18:             {
 19:                 // TODO: Perform some additional logging here
 20:                 throw;
 21:             }
 22:         }
 23: 
 24:         public void WalkDogWithThrowEx()
 25:         {
 26:             try
 27:             {
 28:                 BadDog gd = new BadDog();
 29:                 gd.WalkDog();
 30: 
 31:             }
 32:             catch (Exception ex)
 33:             {
 34:                 // TODO: Perform some additional logging here
 35:                 throw ex;
 36:             }
 37:         }
 38:     }
 39: }
 40: 


BadDog.cs



  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: 
  6: namespace ThrowDemo
  7: {
  8:     class BadDog
  9:     {
 10:         public void WalkDog()
 11:         {
 12:             throw new Exception("An error occurred in the baddog class");
 13:         }
 14:     }
 15: }
 16: 


If the throwWithEx field is set to true, the WalkDogWithThrowEx procedure is called. Inevitably the WalkDog procedure is called, an exception is thrown and is bubbled back up to the Program class. Because we are using the Throw ex syntax the stacktrace is reset locally upon re-throwing the exception and we are left with a shallow stacktrace that indicates the error originated in the DogWalkerClass class at line 35. We know this to be false – since we threw the exception ourselves in the BadDog class.



image



If, on the other hand, the throwWithEx field is set to false, the WalkDogWithThrow procedure is called. Again, the WalkDog procedure is called, an exception is thrown and is bubbled back up to the Program class. This time we are using the Throw ex syntax. The stacktrace is persisted (i.e. not reset) and we are left with the original, deeper, stacktrace that indicates the error originated in the BadDog class at line 12.



image



This is certainly not rocket science – but you’d be surprised how often this is misunderstood or not known at all. Try to envision the additional complexity involved if we had 1,000,000 LOC rather than a paltry 100 LOC. It’s the small things that get you in the end…

1 comment

Popular posts from this blog

Getting Started with Mirth (Part 2)

Mirth

Visual Studio 2012–Debug in Chrome Incognito Mode