传递ExceptionDispatchInfo而不仅仅是Exception有什么意义?

Fun*_*lad 16 .net c# exception-handling

我理解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();
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

,两者都给出了基本相同的堆栈跟踪(它的async变体类似于此.).所以,我真的不明白为什么这个ExceptionDispatchInfo类存在,而不仅仅是一个组合ExceptionDispatchInfo.Capture(e).Throw()方法.

Joe*_*ari 21

您假设异常是不可变的。情况并非如此 - 异常的 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
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我们使用“ throw”而不是“ throw ex”,ex2 堆栈跟踪会处于其初始状态吗? (3认同)
  • 一个 .NET 小提琴 https://dotnetfiddle.net/HnuVvO (2认同)

Hip*_*riX 6

ExceptionDispatchInfo 用于在抛出异常后保留堆栈跟踪,允许您捕获该异常,而不是立即抛出它(作为捕获的一部分),并在以后的某个时刻引发此类异常。

我在https://thorarin.net/blog/post/2013/02/21/Preserving-Stack-Trace上找到了一个很好的例子 。