Friday, May 02, 2008

Rethrowing Exceptions in .NET

I recently learnt after all this time using .NET that rethrowing an exception such as throw ex in a catch block causes the CLR to alter the original exception meaning you will not get the full trace stack in the exception when it is finally bubbled up the chain.

Consider the following code:
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.Foo(true);
}
}

public class MyClass
{
public void Foo(bool includeTraceStack)
{
if (includeTraceStack)
ThrowExceptionIncTraceStack();
else
ThrowExceptionNotIncTraceStack();
}

public void ThrowExceptionIncTraceStack()
{
int a = 0;
int b = 0;

try
{
Divide(a, b);
}
catch (DivideByZeroException)
{
throw;
}
}

public void ThrowExceptionNotIncTraceStack()
{
int a = 0;
int b = 0;
try
{
Divide(a, b);
}
catch (DivideByZeroException ex)
{
throw ex;
}
}

public decimal Divide(int a, int b)
{
return a / b;
}
}
Calling MyClass.Foo(true) gives you the following trace stack:
 at ConsoleApplication1.MyClass.Divide(Int32 a, Int32 b) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 58
at ConsoleApplication1.MyClass.ThrowExceptionIncTraceStack() in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 38
at ConsoleApplication1.MyClass.Foo(Boolean includeTraceStack) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 22
at ConsoleApplication1.Program.Main(String[] args) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 13
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Calling MyClass.Foo(false) gives you the following trace stack:
at ConsoleApplication1.MyClass.ThrowExceptionNotIncTraceStack() in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 52
at ConsoleApplication1.MyClass.Foo(Boolean includeTraceStack) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 24
at ConsoleApplication1.Program.Main(String[] args) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 13
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Notice when throwing the original exception by qualifying the exception ie: throw ex as opposed to just throw clears the trace stack lower down in the calling chain. Just using throw gives us more information in the trace stack.

I must say I have always coded thow ex for clarity, not anymore!

No comments: