The Mistake Every C# Programmer Makes
In the C# projects I have worked in over the last 5 years or so, I see the same mistake being made constantly, often with irritating results when it comes to traking down problems in production systems. The cause of the problem is that most C# programmers I have met doesn't know the difference between throwing an exception, and rethrowing an exception. When an exception is thrown, the frames in the call stack are recorded the Exception.StackTrace property. This provides vital information in tracking down the root problem that caused the exception. The StackTrace property is reset every time an exception object is thrown:
try{
//code here
}
catch(Exception ex){
//some code here related to the exception
throw ex; //Reset the StackTrace to make finding the real problem harder to find
}
When the exception bubbles up to a point where it is handled via detailed logging or caught in a debugger, the StackTrace will only contain information on the stack frames up to the point where the exception is caught and then thrown again. If the exception is not logged sufficiently at each point where it is caught, it can make tracking down the root problem impossible without attaching a debugger. It is also poor practice.
There are two solutions to the problem dont catch the Exception unless you can do something meaningful with it, or, if catching if required, rethrow the same exception. The C# syntax for rethrowing an exception is the throw keyword without an expression. The C# language specification (v1.2) states: A throw statement with no expression can be used only in a catch block, in which case that statement re-throws the exception that is currently being handled by that catch block.
The code presented above can be fixed by removing the expression after the throw statement:
try{
//code here
}
catch(Exception ex){
//some code here related to the exception
throw; //Notice the absence of the ex expression
}
At a MSIL level, a different instruction is used in the two cases. The catch block in the first code same will compile to:
catch [mscorlib]System.Exception
{
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: throw
} // end handler
In contrast, the catch block on second code sample will compile to:
catch [mscorlib]System.Exception
{
IL_000c: stloc.0
IL_000d: rethrow
} // end handler
It is unfortunate that the C# language syntax is less clear and obvious than the MSIL.
An added benefit of not re-setting the stack trace is that the code will run faster, as the unnecessary stack capture is avoided.