Thursday, September 30, 2010

Rethrowing Exceptions Without Losing Original Stack Trace

Everyone knows you should never ‘throw err’:



        // Do something bad


    catch(Exception err)


        // Some error handling, then…

        throw err;



…because you overwrite the original stack trace, and end up with no idea what happened where. If you want to re-throw, you just ‘throw’ within the catch block, and the original exception is re-throw unmodified (or wrap-and-throw).

But that’s within the catch block. What do you do if you need to re-throw an exception outside the catch, one you stored earlier? This is exactly what you have to do if you’re implementing an asynchronous (APM / IAsyncResult) call, or marshalling exceptions across app domain / remoting boundaries.

The runtime manages this just fine by ‘freezing’ the exception stack trace. When rethrow, the new stack trace is just appended to the old one – that’s what all those ‘Exception rethrow at [0]’ stuff is in the stack trace. But the method it uses to do this (Exception.PrepForRemoting) is internal. So unfortunately in order to use it, you have to call it by reflection:

    public static void PrepForRemoting(this Exception err)




            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,

            (Binder)null, err, new object[0]);



    /// <summary>

    /// Rethrow an exception without losing the original stack trace

    /// </summary>


    public static void Rethrow(this Exception err)



        throw err;


Evil I here you cry? Well suck it up, because that’s exactly what Rx does in System.CoreEx:


(Tasks in .Net 4 side-step this problem by always wrapping exceptions in a new AggregateException prior to throwing – this also allows a Task to accumulate multiple exceptions throughout its lifecycle, depending on the continuations applied)

No comments:

Popular Posts