Tuesday, November 01, 2005

"throw" and "rethrow"

Few days back I was working on some C# code, and I found an interesting case .. all I was thing to throw an exception from an called routine to the caller and logging the exception in the caller, and I found the stack trace is not correct( more specifically the stack trace was missing some entries ).
I had to spend some time on this to find the reason behind it ... let me explain with some sample code snippet ..

private void btnTest_Click(object sender, System.EventArgs e)
{
try {
DummyMethod_1();
} catch(Exception ex) {
richTextBox1.Text = ex.GetType().ToString() + " " + ex.Message + "\n";
richTextBox1.Text += ex.StackTrace;

}
}

private void DummyMethod_1()

{
try {
DummyMethod_2();
} catch(Exception ex)
{
MyNewException e = new MyNewException ("My New exception" );
throw e;
}
}

private void DummyMethod_2()

{
ThrowException();
}

private void ThrowException()

{
int x = 0;
int y = 0;
int z = x/y;
}

btnTest is a command button and I am calling a routine from the click event of this routine, then there is a chain of routines .. which ultimately called "ThrowException" routine and which cause an exception and the "DummyMethod_1" catch the exception and send a new exception "My New Exception" to the caller ( throw e ).
I am showing the stack trace of the exception in a rich text box ...
with the above code the stack trace will look like this ...
System.Exception.MyNewException My New exception at ThrowTest.Form1.DummyMethod_1() in f:\personal\rnd\throwtest\throwtest\form1.cs:line 127 at ThrowTest.Form1.btnTest_Click(Object sender, EventArgs e) in f:\personal\rnd\throwtest\throwtest\form1.cs:line 102

All it was showing btnTest_Click::DummyMethod_1(); and the DummyMethod_1:: throw e; ... what about the DummyMethod_2 and ThrowException method ??? as they were also part of this exception in fact "ThrowException" is the routine where the exception was generated.

Then I have changed my code little bit .. instead of throw e; I modified it as throw; now the stack trace looks like this ..
System.DivideByZeroException Attempted to divide by zero. at ThrowTest.Form1.ThrowException() in f:\personal\rnd\throwtest\throwtest\form1.cs:line 140 at ThrowTest.Form1.DummyMethod_2() in f:\personal\rnd\throwtest\throwtest\form1.cs:line 133 at ThrowTest.Form1.DummyMethod_1() in f:\personal\rnd\throwtest\throwtest\form1.cs:line 127 at ThrowTest.Form1.btnTest_Click(Object sender, EventArgs e) in f:\personal\rnd\throwtest\throwtest\form1.cs:line 102

So what is the mystery of "throw e" and "throw", if we look at the MSIL , in case of thorw e .. the keyword is "throw" ... it creates a new exception( e in this case ) which has a separate stack trace altogether, but in case of thorw .. the MSIL is "rethrow" which re-thorws the existing exception, which means the stack trace will be intact.

hmmmm .. isn't it interesting ?? .. well, there is another way to tackle this situation when we will be creating a new exception we should pass the existing one as an inner exception , like this ..
MyNewException e = new MyNewException ("My New exception", ex ); and in the caller we have to look into the inner exception's stack trace as well, so the updated stack trace showing code will look like this ...
richTextBox1.Text = ex.GetType().ToString() + " " + ex.Message + "\n";
if( ex.InnerException != null )

{
richTextBox1.Text += ex.InnerException.StackTrace + "\n" + ex.StackTrace;
} else
{
richTextBox1.Text += ex.StackTrace;
}

comments are welcome !!!!

1 comment:

Neo said...

Some more twist into the story ..
what hapeens if insetead of creating a new exception we should pass the existing exception from DummyMethod_1 ..
Let's have a look at the code snippet...
private void DummyMethod_1()
{
try
{
DummyMethod_2();
}
catch(Exception ex)
{
//Exception e = new Exception("My New exception" );
throw ex;
}
}
guess what would happen if we modify the code in the above manner .. the same thing would happen .. it will show the following stack trace ..
System.DivideByZeroException Attempted to divide by zero.
at ThrowTest.Form1.DummyMethod_1() in f:\personal\rnd\throwtest\throwtest\form1.cs:line 127
at ThrowTest.Form1.btnTest_Click(Object sender, EventArgs e) in f:\personal\rnd\throwtest\throwtest\form1.cs:line 102.

Reason is throw ex .. is override the existing stace trace. hence whether we create a new exception or pass the existing one .. we will face the same problem.