FunctorSalad
FunctorSalad

Reputation: 2622

What's the point of passing ExceptionDispatchInfo around instead of just the Exception?

I understand the value of ExceptionDispatchInfo.Capture(e).Throw() (preserves the original stack trace), but what's the advantage of using Capture early and passing the ExceptionDispatchInfo around vs. just passing the caught Exception around?

As a concrete example, comparing

static Exception CaptureException(Action action)
{
  try
  {
    action();
    return null;
  }
  catch (Exception e)
  {
    return e;
  }
}

public void Test1()
{
  ExceptionDispatchInfo.Capture(CaptureException(
       () => throw new IOException("Test")))
    .Throw();
}

with

static ExceptionDispatchInfo CaptureDispatchInfo(Action action)
{
  try
  {
    action();
    return null;
  }
  catch (Exception e)
  {
    return ExceptionDispatchInfo.Capture(e);
  }
}

public void Test2()
{
  CaptureDispatchInfo(() => throw new IOException("Test")).Throw();
}

, both give essentially the same stack traces (It's similar for an async variation of this.). So, I don't really get why the ExceptionDispatchInfo class exists at all, rather than just a combined ExceptionDispatchInfo.Capture(e).Throw() method.

Upvotes: 35

Views: 12934

Answers (2)

HiperiX
HiperiX

Reputation: 407

ExceptionDispatchInfo is used to preserve the stack trace after an Exception is thrown, allowing you to catch that exception, not throwing it immediately (as part of a catch), and to raise such exception on a later point in the future.

I found a good example of this on https://thorarin.net/blog/post/2013/02/21/Preserving-Stack-Trace.

Upvotes: 6

Joe Albahari
Joe Albahari

Reputation: 30954

You're assuming that exceptions are immutable. This is not the case - an exception's StackTrace changes when it's re-thrown.

The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time:

void Foo() => throw new InvalidOperationException ("foo");

Exception original = null;
ExceptionDispatchInfo dispatchInfo = null;
try
{
    try
    {
        Foo();
    }
    catch (Exception ex)
    {
        original = ex;
        dispatchInfo = ExceptionDispatchInfo.Capture (ex);
        throw ex;
    }
}
catch (Exception ex2)
{
    // ex2 is the same object as ex. But with a mutated StackTrace.
    Console.WriteLine (ex2 == original);  // True
}

// So now "original" has lost the StackTrace containing "Foo":
Console.WriteLine (original.StackTrace.Contains ("Foo"));  // False

// But dispatchInfo still has it:
try
{
    dispatchInfo.Throw ();
}
catch (Exception ex)
{
    Console.WriteLine (ex.StackTrace.Contains ("Foo"));   // True
}

Upvotes: 41

Related Questions