Какой смысл передавать ExceptionDispatchInfo вместо только Exception?

Я понимаю значение ExceptionDispatchInfo.Capture(e).Throw() (сохраняет исходную трассировку стека), но каково преимущество использования Capture раннем этапе и передача ExceptionDispatchInfo по сравнению с просто пропусканием Exception?

В качестве конкретного примера, сравнивая

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();
}

с

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();
}

оба дают по существу одинаковые трассировки стека (это похоже на async изменение этого.). Таким образом, я действительно не понимаю, почему класс ExceptionDispatchInfo существует вообще, а не только комбинированный метод ExceptionDispatchInfo.Capture(e).Throw().

Ответ 1

ExceptionDispatchInfo используется для сохранения трассировки стека после создания исключения, что позволяет вам перехватить это исключение, не выбрасывая его немедленно (как часть перехвата), и вызвать такое исключение на более позднем этапе в будущем.

Я нашел хороший пример этого на https://thorarin.net/blog/post/2013/02/21/Preserving-Stack-Trace.aspx.

Ответ 2

Вы предполагаете, что исключения являются неизменными. Это не тот случай - исключение StackTrace изменяется при повторном вызове.

Целью ExceptionDispatchInfo.Capture является захват потенциально мутирующего исключения StackTrace в определенный момент времени:

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
}