Here is what I learnt from Jackie Goldstein's talk on .NET Worst Practices at the .Net Deep Dive conference in Tel-Aviv last Thursday. There is a subtle, but hugely important difference between how .NET and Java re-throw a caught exception and I missed that somehow when been learning .NET. Not that I didn't know what "throw;" does in C#, I was mistaken about what "throw ex;" does!
In Java, when you do "throw ex;", ex is being re-thrown as if it wasn't caught at all - no informantion about re-throwing is ever recorded and original stack trace info is preserved. If you do want to start exception's stack trace from the re-throwing point - oh, that's completely different story, you need to refill exception's stack trace using fillInStackTrace() method.
In .Net however, when you do "throw ex;", ex is being re-thrown, but the original stack trace info gets overriden. The point where exception is re-thrown is now becoming the exception's origin. Here is what I mean. If you do follow your Java habits and write
using System; public class MyApp { public static void F() { throw new NotImplementedException("Too lazy to implement!"); } public static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception {0} has occured!", e.GetType()); throw e; //Line 18 } } }you'll get:
Exception System.NotImplementedException has occured! Unhandled Exception: System.NotImplementedException: Too lazy to implement! at MyApp.Main() in d:\projects\test\class2.cs:line 18See, you've lost the original exceptions's stack trace and now you gonna have really hard time to figure out what was wrong actually, where the exception was thrown at the first place.
So in .NET you have to use "throw" keyword ("Throw" in VB.NET) with no argument to perform a pure re-throwing of an exception - change the line 18 to just "throw;" and the result will be
Exception System.NotImplementedException has occured! Unhandled Exception: System.NotImplementedException: Too lazy to implement! at MyApp.F() in d:\projects\test\class2.cs:line 6 at MyApp.Main() in d:\projects\test\class2.cs:line 18Now you can see the full exception stack trace.
Basically MSIL (CIL) has two instructions - "throw" and "rethrow" and guess what - C#'s "throw ex;" gets compiled into MSIL's "throw" and C#'s "throw;" - into MSIL "rethrow"! Basically I can see the reason why "throw ex" overrides the stack trace, that's quite intuitive if you think about it for a moment. But "throw" syntax for "rethrow" instruction is not really intuitive. It smells stack based MSIL, which is obviously under the cover, but actually should be kept there. I guess they wanted to keep number of C# keywords small, that's the reason. So you just better know this stuff - use "throw;" to re-throw an exception in .NET.
This is article simply superb to understand the difference between throw and throw ex.
Thanks..
Sander Aiaots, I can't reproduce your problem. Works for me as in the article...
I tested this "throw;" functionaliti in VS2005 and .Net 2.0, and in my case throw; == throw ex;
Both cases .Net rewrites my stack trace and I can't follow real error.
this is code from form:
In it's method:
clsValidator val = new clsValidator();
try{ val.check();
}
catch
{
textBox1.Text = "Caught";
}
public class clsValidator
{
public void check()
{
try
{ throw new ExceptionMyException");
}
catch
{ throw;
}
finally
{
}
}
}
I get the Exception in method where I am calling the check method...but if I comment the throw statement in catch part of it...exception doesn't regenarate....that is in form method no catch exeution....
Good article and to the point to understand.
It is simply superb.provide some more articles.You just make us think like this.ThanQ
Simply the best clarification.
I got cleared my doubt.
Thanks
This is a great article. Throw ex versus Throw, wow, this is wonderful.
God bless .net
Poor soles that created this powerful exception handling should sue or have legal issues towards Microsoft.
Probably former employees at Sun... who knows...
Good article, very short and to the point. Told me exactly what I needed to know.
Thanks
[Reverse liamg to gmail for sending mails]
If you carefully check the stack trace, you will see that "throw;" (re-throw) still wipes out one line of the stack.
If you did not catch at all, then the stack trace would show line 6 and line 13.
Rethrowing swaps the second step to line 18 instead. (This could be an issue if, for example, you call F() twice inside the try -- you won't know which failed.)
If you wrap in an outer exception (as suggested by Jeff), then you get both stack traces: one with lines 6 & 13, and the second with line 18.
Oleg Tkachenko just didn't get it -- the snippet he posted contains the stack wipe error.
I don't know what Oleg finds "complicated", but I tend to find debugging where some dingbat has wiped my stack trace a pain!
You MIGHT want to re-throw the exact same exception if you are for example calling a method in another class that does data access and opens DB connections.
eg. Class Billy
public void hello
{
try
{
DataAccessClass.addWelcome();
}
catch(Exception ex1){BLABLALBA}
}
addWelcome()
{
try
{
open db
blabla
close db
}
catch(Exception ex1)
{
close db !
rethrow exception for other methods to catch
}
}
cheerio
Well, going with inner exeptions is too complicated usually.
It's very common practice to re-throw an exception after some logging stuff done. I mean the following pattern:
try {
doSomeJob();
} catch (Exception e) {
logger.error("Job failed miserably");
throw e;
}
Well, this shouldn't come up much in practice, because..
A) if you're re-throwing, you should be re-throwing as a new *more informative* exception most of the time. Eg the existing exception becomes the inner exception:
Throw New SpecificException("more info", e)
Nothing is lost in this case; you've got two stack traces and two exceptions.
B) if you're catching, you should catch specific exception types (possibly for fix/consumption) and ignore the rest, so they fall out without any specific throw.
Dunno, I rarely if ever need to blindly rethrow the same exact exception.