When .NET was being designed and introduced, it was notable not only in the ways in which it “borrowed” from Java, but also in the ways in which it parted from it. Some decisions were obviously good (dumping checked exceptions comes to mind) while some were clearly troubling (non-virtual methods by default).
Some, though, seemed insignificant at the time until you come up against them. Today’s example:
When is the stack trace for an exception generated?
In Java, the stack trace is generated when an exception is created, and in .NET it’s generated when the exception is thrown. In practical terms, with 99% of throws looking like this, it hardly matters:
throw new SomeException();
The creation and the throw actually happen on the same line of code. The stack trace is identical in both cases.
However, this seemingly small decision has rippling impact throughout the framework and languages. How many times have you seen this code and cringed?
catch(Exception ex)
{
DoSomething();
throw ex;
}
We’ve all seen it. That “throw ex” line destroys the original stack trace and replaces it with your own. The C# language team even had to invent a way to “re-throw an exception without destroying the original stack trace”, which is “throw;” on a line all by itself.
This probably should’ve been the clue that the wrong decision was made.
It’s reasonable to think that there might be a time when you have an exception object, outside of a catch block, and you want to re-throw that exception as though it were never caught. In fact, the framework team themselves came across just such a situation and invented their own hack to handle it, and built it into the Exception class.
When you are talking to a class via remoting, an exception thrown by the remote component is caught by the remote end and serialized back to the transparent proxy. The transparent proxy (which is what the client code is talking to) needs to experience that exception just as though it had been thrown locally. The problem is, we’re not in a catch block; we have a hydrated exception, but need to re-throw it without trashing the stack trace.
There is a private field inside of Exception into which you can stuff a stack trace which says “when I throw you, this is your stack trace; don’t make a new one”. This sample helper method shows you how to do the work:
public static void RethrowWithNoStackTraceLoss(Exception ex)
{
FieldInfo remoteStackTraceString =
typeof(Exception).GetField("_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic);
remoteStackTraceString.SetValue(ex, ex.StackTrace);
throw ex;
}
I first ran across this trick via Peter Provost pointing me to Chris Taylor’s blog post on the subject. I’ve used it several times now (in ObjectBuilder and xUnit.net), and I’m thankful for it every time I run across it.
Since we’re talking about exceptions…
One of my biggest gripes about exceptions relates to reflection. The remoting team obviously went to great lengths to hide the fact that you were talking to a transparent proxy instead of the real object. That’s really the whole reason this hack exists in the first place. You can treat a transparent proxy just like the real thing.
It’s not so easy with Reflection. If you invoke something through reflection, and it throws an exception, the reflection infrastructure catches that exception and wraps it in TargetInvocationException. It’s basically an “I was here!” exception: the thing you actually care about it partially obscured by it. Nobody ever really cares about TargetInvocationException; it’s just a reminder that you’re doing things differently than normal, and you can’t treat some thing like you normally would.
I use this re-throw trick when reflection is an unimportant implementation detail (which it almost always is). For example, when running a test in xUnit.net, the test method is invoked via reflection; the person writing the test shouldn’t ever care about that detail. If every exception that was thrown in a test was reported as a TargetInvocationException, I’m pretty sure people would be telling us it was a bug.
So our implementation of TestCommand.Execute() uses this code instead:
public MethodResult Execute(object testClass)
{
try
{
testMethod.Invoke(testClass, null);
}
catch (TargetInvocationException ex)
{
ExceptionUtility.RethrowWithNoStackTraceLoss(ex.InnerException);
}
return new PassedResult(testMethod, Parameters);
}
We originally spun in a loop at the point where we caught exceptions and turned them into FailedResult, stripping off any TargetInvocationExceptions that were contained. But that made testing for TargetInvocationException difficult in some cases, because you couldn’t differentiate between a TargetInvocationException you didn’t care about from one you did.
As such, everywhere that we use reflection, you’ll find a try/catch like this, eradicating the less-than-useless TargetInvocationException from the exception stack. Every time we have to write it, it makes me wish the reflection team had just done the right thing in the first place.
Recent Comments