I've coached many youth baseball teams, and I've always put a lot of focus on developing pitchers. Your left fielder can be chasing butterflies, and the right fielder might have his glove off, and the third baseman can be making faces at his sister in the stands...but if the guy on the mound is mowing 'em down, your odds of winning are pretty good. To the uninitiated, pitching looks easy, but in fact it is one of the most difficult skills in all of sports. To throw a baseball with both force and accuracy requires the balance of a dancer on stage, the timing of NASCAR driver weaving through traffic, and the consistency of a pro golfer on the driving range. These do not come easy to a nine year old, so Coach Falter has spent a lot of hours helping aspiring pitchers learn to throw a ball.
But today Coach Falter is going to explain the secret of catching and throwing something else: an exception. Our first major web project had a lot of exception handling code that looked like this:
public static void Save(QuoteDS dsQuote)
{
IDbConnection conn= DataHelper.NewConnection(DataHelper.ConnectionString);
IDbTransaction trn=null;
try
{
conn.Open();
trn = conn.BeginTransaction(IsolationLevel.ReadCommitted);
// ******************************************************************
// Do a transacted set of updates, using data in the dataset dsQuote
// ******************************************************************
// data update code here...
// Commit the transaction
if (trn != null)
trn.Commit();
}
catch (Exception ex)
{
if (trn != null)
trn.Rollback();
throw ex;
}
finally
{
conn.Dispose();
}
}
This code needs an exception handler in order to rollback any in-flight transaction. On the other hand, it needs to rethrow the exception so that a handler further down the stack can catch it, log it, and display an appropriate message to the user. This is the exception-handling pattern blessed by Microsoft for all non-masochistic programmers. (Masochistic programmers prefer to set up exception handlers in every function, and build an elaborate scheme of return codes and switch statements and Lord knows what else.)
However, the above code has a hidden flaw. I'll grab a cup of coffee while you examine the code carefully....
Okay, you've been patient enough, it's time for your reward. When the .NET Framework executes this statement:
throw ex;
it throws away all the stack information above the current function. So when you look at your error log, you won't know which line of code was executing when the exception was thrown. Sometimes this won't be a bother, but other times you will spend fruitless hours speculating on what might have happened. So we need a better approach.
Here's how you retain the full stack trace: just rethrow the exception like this...
throw;
That's right, all you need to do is remove one little word. Superficially, it is no more different from "throw ex" than the throwing motions on a change-up and a fastball. (Hint: they are supposed to be identical; the difference is in how the pitcher holds the ball.) But like the difference between a fastball and a change-up, the difference between "throw" and "throw ex" becomes obvious very quickly! So get out those code editors and start throwing like you're supposed to...and lift your knee almost up to your elbow during the wind-up....and step toward home plate....
EDIT 3/28/2007: Based on a reader's comment and further research, I have updated my advice. See "More on How To Re-throw an Exception" for the latest and greatest.