C# 什么';传递ExceptionDispatchInfo而不仅仅是异常的意义是什么?

C# 什么';传递ExceptionDispatchInfo而不仅仅是异常的意义是什么?,c#,.net,exception-handling,C#,.net,Exception Handling,我理解ExceptionDispatchInfo.Capture(e).Throw()(保留原始堆栈跟踪)的价值,但是尽早使用Capture并传递ExceptionDispatchInfo与只传递捕获的异常相比有什么好处 作为一个具体的例子,比较 static Exception CaptureException(Action action) { try { action(); return null; } catch (Exception e) {

我理解
ExceptionDispatchInfo.Capture(e).Throw()
(保留原始堆栈跟踪)的价值,但是尽早使用
Capture
并传递
ExceptionDispatchInfo
与只传递捕获的
异常
相比有什么好处

作为一个具体的例子,比较

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


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

ExceptionDispatchInfo用于在抛出异常后保留堆栈跟踪,允许捕获该异常,而不是立即抛出它(作为捕获的一部分),并在以后提出此类例外情况

我在网上找到了一个很好的例子
.

您假设异常是不可变的。事实并非如此-异常的堆栈跟踪在抛出时会发生更改

ExceptionDispatchInfo.Capture
的目的是在某个时间点捕获可能发生变异的异常的堆栈跟踪:

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
}

这是一个运行时实现细节。理想情况下,此类类是
内部的
,但它用于多个框架程序集中,因此它们别无选择,只能将其公开。这要求它们在MSDN中记录,但不要求它们告诉您它在您自己的代码中有何用处。许多框架类类似于我认为它解决的关键问题是异常堆栈跟踪具有线程亲和力,特别是Control.Invoke()的一个大问题。只要您使用框架提供的机制编写异步代码,您就会从它的存在中获益。如果您有一个库保存引发的异常并在以后抛出它,这将有助于保留原始stacktrace。还有一个方法InternalPreserveStackTrace,可以调用,这是另一种解决方法(我想是以前例外的DispatchInfo存在)。
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
}