Quantcast
Channel: This Title Intentionally Left Blank » Programming
Viewing all articles
Browse latest Browse all 10

Just throw;ing this out there.

$
0
0

In C#, what’s the difference between “throw;” and “throw e;”?

This is one of those “Interview Question” tidbits of language trivia.  It’s the sort of thing that you can probably remember the exact circumstances where you learned about it, because it was probably preceded by three hours of frustration and confusion.  It’s the sort of thing that I’d always assumed separated the serious C# programmers from the casual passerby.

Until today…

Today, this issue came up and I was honestly surprised by the number of people around me who didn’t know the difference, and even more surprised about how the difference seems to be missing from official sources, like the C# Language Specification1, so I felt compelled to write about it.

To clarify, I’m asking about the difference between “throw;” and “throw e;”, where you’re rethrowing an exception that was caught inside a catch statement.

In other words, this:

public static void DoSomething()
{
    throw new Exception("Thrown on line 12.");
}

public static void ExceptionRethrower()
{
    try
    {
        DoSomething();
    }
    catch (Exception e)
    {
        LogException(e);
        throw e;
    }
}

So, DoSomething(); does something and throws an exception2.  You want to catch it and log it or whatever, but you want the exception to bubble up to the callers of ExceptionRethrower() for some reason.  I’m sure you’ve written code like this somewhere.

And there it is, “throw e;” at the end of the catch.  Let’s write a bit of code to call this function and catch the exception, and see what turns up.

static void Main(string[] args)
{
    try
    {
        ExceptionRethrower();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}

Okay…  So, the function DoSomething is throwing an exception, which is caught and logged in ExceptionRethrower, then rethrown up to Main, where the exception and its stack trace are printed out.  That means the stack trace will have DoSomething on top, then ExceptionRethrower, followed by Main on the bottom, right?

System.Exception: Thrown on line 12.
   at ThrowExample.Program.ExceptionRethrower() in E:\svn\Projects\ThrowExample\ThrowExample\Program.cs:line 26
   at ThrowExample.Program.Main(String[] args) in E:\svn\Projects\ThrowExample\ThrowExample\Program.cs:line 34

Nope.

It’s got Main and ExceptionRethrower, but what happened to DoSomething?  And the exception message says that I threw it on line 12, but according to the stack trace, I was nowhere near line 12.

So…  WTF?

Let’s play with the debugger, instead.  I’m going to comment out the try/catch in Main and let the exception fall out and kill my app and see what VS has to say about it.

Line 26 is my “throw e;”.  It’s eating my stack trace!

Okay, so, let’s see what happens if I just “throw;”, instead.

System.Exception: Thrown on line 12.
   at ThrowExample.Program.DoSomething() in E:\svn\Projects\ThrowExample\ThrowExample\Program.cs:line 12
   at ThrowExample.Program.ExceptionRethrower() in E:\svn\Projects\ThrowExample\ThrowExample\Program.cs:line 26
   at ThrowExample.Program.Main(String[] args) in E:\svn\Projects\ThrowExample\ThrowExample\Program.cs:line 34

Hey, look!  DoSomething() is there in the trace now, with the exception originating on line 12 like it should.

So, there’s your answer to the original question:  Rethrowing the instance, as in “throw e;” will discard the existing stack trace and make it look like the exception originated on the “throw e;” line, while a parameterless “throw;” will retain the original stack trace.

Sort of…

Look at that second stack trace again.  Look closely at the line numbers.  It still has line 26, where the “throw;” is located, but the call to DoSomething() is on line 21.  It should, like the call stack below shows, have DoSomething() line 12 (Where the exception was thrown), then ExceptionRethrower() line 21 (Where DoSomething() was called), then Main() line 34 (Where ExceptionRethrower() was called).

How did line 21 get tossed aside for line 26?  Well, think about the stack for a moment.  When you’re generating a stack trace, either on the call stack side or on the exception side, each function has a single frame, pointing at the last line that was executed.  When you’re at line 12 in DoSomething(), the last line in ExceptionRethrower() was line 21.  However, once the exception is thrown, you hit the catch in ExeceptionRethrower() and get rerouted, so by the time you end up in Main(), the last line called in ExceptionRethrower() was line 26 at the throw;.  This also means that if you throw, catch, and rethrow, all in a single function, the stack trace will end up pointing at the rethrow, regardless of whether you’re using “throw;” or “throw e;”.

Of course, if that whole “call stack” explanation I just gave actually held any water, then adding in a finally block would cause the stack trace to point at the last line of the finally, because that was the last line of the function executed.  However, that’s not the case.  Even when you add in the finally, the stack trace still points at the throw line.   But hey, the explanation still sounds plausible, so I’m leaving it in this post.  I’m sure it’s something close to that, anyway.  It’s a good thing finallys don’t affect the trace, because if it did, I can’t imagine how wildly FUBARed some stack traces would end up. 3

In general, if you have to catch and rethrow an exception, you should use the plain throw;.  If you rethrow the instance using throw e;, you’ll have a hard time debugging and testing, because your stack traces will all dead end at a throw that’s nowhere near the source of the problem.  However, if you’re writing a library or service for third-party consumption, then throw e; provides a quick way to let exceptions get out to the callers while hiding some of the implementation details of your code by killing off the call stack. 4

Another thing to be aware of is that whichever rethrowing scheme you use, the original instance is used.  That means that if you save off the exception instance for later, the stack trace will have been wiped out or modified by the time you want to use it.  Even more subtle and insidious is that this means that if you’re using a threaded logger of some sort, there’s a chance that the stack trace will be mangled by the time the logger gets around to writing out the exception.  In other words, some times the log will say the exception’s on line 12, other times it’ll say the exception’s on line 26. 5

The sample code is here: http://www.mathpirate.net/svn/Projects/ThrowExample/

To reiterate, for those who skipped to the end:

  • “throw e;” wipes out the stack trace, restarting it at the point of the throw.
  • “throw;” maintains the existing stack trace, but still mangles the stack of the current frame.
  1. I couldn’t find it in there, anyway…
  2. From line 12, obviously…
  3. By the way, speaking of finally blocks, did you know that you can’t return from one?  There’s another bit of obscure language trivia for you.
  4. Of course, if you actually think that’s a good idea, then you probably will want to rethink your exception handling strategy in general…  That’s just the only reason I could think of for deliberately using throw e; to rethrow a caught exception.
  5. Have fun debugging that one…

Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images