When the first beta of the .Net framework was released one of the best features I thought was that the Exception.ToString() method shows you the stack trace from the place where the exception is thrown to where it is caught. Stack traces are your best friend when finding the cause of an unexpected exception. Back in the VB 6 time we spent a lot of effort harvesting this information ourselves.
This is why I get really upset when someone screws up the stack trace information by doing something like the following.
try
{
// method call that might throw an exception at some deeper level
some.Method();
}
catch (Exception ex)
{
// some code to figure out what to do with the exception (maybe logging or whatever)
// hmm in this case we do not really handle this exception after all, lets re-throw
throw ex;
}
What is wrong with this code? It is the 'ex' behind the throw. When you re-throw an exception this way the stack trace information in the Exception object is cleared and it will appear to who ever catches it at a higher level as if the exception originated from the location where it is re-thrown, thereby loosing valuable information about the location where the exception really occurred.
Inside a catch block you are allowed to use trow; (without an exception). The C# reference doesn't say very much about this, except that it is used when re-throwing the current exception object in a catch clause. Sounds like it is pretty much the same as when you do explicitly throw the exception you just caught as in the code example.
There is however a big difference that is not directly clear from the documentation. When you use throw without an exception you are actually saying "lets pretend I never caught this exception". This causes the Exception to be thrown further up while preserving the whole stack trace from the original throw to the next catch up the call stack.
Now pretty please, with sugar on top, use throw; instead of throw ex; inside a catch block, and leave my stack trace alone!
8 comments
FxCop will inform you about this as well.
robertka
I've tried both 'throw' and 'throw e' they produce the same result .. means : The stack trace is reseted.
I use it with Visual Studio 2005.
So I'm lost ….
Arif
I think someting more is going on in this case. Are you sure there is a stacktrace to begin with? You could check this by setting a breakpoint in the catch and inspect the StackTrace property in the watch window before it is retrown. Are you doing anything special in the catch block regarding to the exception you are re-throwing?
frankb
Actually, throw; does slightly modify the stack trace: The top frame, to be exact. The code below demonstrates this.
using System;
public class C
{
public static void Main(string[] args) {
try
{
Rethrow();
}
catch (Exception e)
{
Console.Out.WriteLine(“Rethrown exception is ” + e);
// output original stack trace except the Rethrow() stack frame frame now indicates the line where the exception was rethrown rather than the line where the exception was thrown
}
Console.ReadKey();
}
static void Rethrow()
{
try
{
throw new Exception();
}
catch (Exception e)
{
Console.Out.WriteLine(“Original exception is ” + e); // outputs original stack trace
throw;
}
}
}
The Nobb
The Nobb is right, thanks for clearing that up! Rethrow does modify the stacktrace, but it is not as bad as it looks, you only loose one line of information and get a line for the rethow in return
I modyfied your code a bit to make it more clear what happens when the Exeption comes from a deeper than the catch block, (also you had forgotten the actual throw; statement in the sap.
This sample demonstrates that it is not naccaraly the top frame that gets modified, but the line of the catch is replaced by the line of the throw.
using System;
public class C
{
public static void Main(string[] args)
{
try
{
Rethrow();
}
catch (Exception e)
{
Console.Out.WriteLine(“Rethrown exception is ” + e);
// output original stack trace except the Rethrow() stack frame frame now indicates the
// line where the exception was rethrown rather than the line where the exception was thrown
}
Console.ReadKey();
}
static void Rethrow()
{
try
{
ThrowException();
}
catch (Exception e)
{
Console.Out.WriteLine(“Original exception is ” + e);
throw;
// outputs original stack trace throw;
}
}
static void ThrowException()
{
throw new Exception();
}
}
see the output here
Original exception is System.Exception: Exception of type ‘System.Exception’ was thrown.
at C.ThrowException() in C:\Program.cs:line 35
at C.Rethrow() in C:\Program.cs:line 23
Rethrown exception is System.Exception: Exception of type ‘System.Exception’ was thrown.
at C.ThrowException() in C:\Program.cs:line 35
at C.Rethrow() in C:\Program.cs:line 28
at C.Main(String[] args) in C:\Program.cs:line 8
frankb
So, according to the Rethrow example, the original stack trace is lost even with “throw;”?
If you use
throw new Exception(“new message”, ex);
instead of
throw;
then the original trace should be nested. But, this seems very annoying to me that the original exception can not be re-thrown without either losing the original stack trace or creating a new exception.
Johnny
So, according to the Rethrow example, the original stack trace is lost even with “throw;”?
If you use
throw new Exception(“new message”, ex);
instead of
throw;
then the original trace should be nested. But, this seems very annoying to me that the original exception can not be re-thrown without either losing the original stack trace or creating a new exception.
Johnny
The Throw; will change one frame of the stacktrace, that is the frame of the method which contains the catch(Exception) {trow;}. This frame will now point to the line that contains the throw instead of the line that contains the call that actually caused the exception.
The rest of the stacktrace, leading from the original cause to this catch will remain as it was.
There is however another way to keep the stacktrace in tact (poking in the Exception object with relfection). I’ll see if I can do a post on that in the near future.
frankb