【问题标题】:How to throw exception without resetting stack trace?如何在不重置堆栈跟踪的情况下抛出异常?
【发布时间】:2010-10-18 08:02:18
【问题描述】:

这是Is there a difference between “throw” and “throw ex”的后续问题?

有没有办法在不重置堆栈跟踪的情况下提取新的错误处理方法?

[编辑] 我将同时尝试“内部方法”和 Earwicker 提供的另一个 answer,看看哪个可以更好地标记答案。

【问题讨论】:

  • “提取新的错误处理方法”是什么意思?
  • 其实我更感兴趣的是如何模拟“throw;”在提取的错误处理方法中。
  • 你不能“投掷”;在提取的方法本身内部,因为该方法也可以在不来自 catch 块的情况下调用(例如,使用“new Exception()”作为参数调用它)。实现这一点的唯一方法可能是我在返回码的回答中建议的方法。

标签: c# .net exception-handling error-handling stack-trace


【解决方案1】:

是的;这就是 InnerException 属性的用途。

catch(Exception ex)
{
    throw new YourExceptionClass("message", ex);
}

这将允许您添加自己的逻辑,然后抛出您自己的异常类。 YourExceptionClass 实例的 StackTrace 将来自此代码块,但 InnerException 将是您捕获的异常,以及之前的 StackTrace。

【讨论】:

  • 这种方法在实践中的缺点是许多错误日志只包含最顶层异常的详细信息。 This approach 避免了这种情况。
【解决方案2】:

您不想使用原始堆栈跟踪创建新异常。这是一种误导,因为该堆栈跟踪没有创建新的异常。

但是,您可以将原始异常作为“InnerException”放入新异常中。这会满足您的要求吗?

【讨论】:

  • 我想模仿“throw;”在提取的错误处理方法中。
【解决方案3】:

不确定你是不是这个意思,但我在你的另一个问题中的建议是解决这个问题。

如果您的处理程序返回一个布尔值,无论是否处理了异常,您可以在您的 catch 子句中使用它:

catch (Exception ex) {
  if (!HandleException(ex)) {
    throw;
  }
}

【讨论】:

  • +标记为答案:似乎最容易将错误追溯到源头并且易于阅读。顺便说一下,我的“HandleException”方法被命名为“TryHandleRecoverableException”。
  • 如果错误是在与 catch 块相同的堆栈级别引发的(例如,NullReferenceException 将被搞砸),这仍然会搞砸堆栈跟踪。见stackoverflow.com/a/11284872/1739000
【解决方案4】:

您是否捕获了想要更仔细地过滤的异常,以便您可以改变主意,决定不处理它们并重新抛出它们?

如果您想对此非常小心,那并不是一个好主意。最好一开始就不要捕获异常。原因是给定的try/catch 处理程序不应决定运行嵌套的finally 块以处理它不希望看到的异常。例如,如果有一个NullReferenceException,继续执行任何代码可能是一个非常糟糕的主意,因为它可能会导致另一个这样的异常被抛出。而finally 块只是代码,就像任何其他代码一样。一旦第一次捕获到异常,try/catch 下面的堆栈上的任何finally 块都将被执行,此时为时已晚 - 可能会生成另一个异常,这意味着原始异常是丢了。

这意味着(在 C# 中)您必须为要捕获的所有异常类型仔细编写单独的 catch 处理程序。这也意味着您只能按异常类型进行过滤。这有时是很难遵循的建议。

应该可以通过其他方式过滤异常,但在 C# 中则不然。但是,在 VB.NET 中是可能的,而 BCL 本身通过在 VB.NET 中编写少量代码来利用这一点,因此它可以以更方便的方式过滤异常。

Here's a detailed explanation, with a VB.NET code sample, from the CLR team blog.

And here's my two cents.

【讨论】:

  • “您是否捕捉到了想要更仔细地过滤的异常,以便您可以改变主意,决定不处理它们并重新抛出它们?” -> 是的。
  • "finally" 代码总是被执行,无论是否发生异常以及如何处理异常。我没有看到与问题的关系......
  • +1 链接.. VB 拥有而 C# 没有的有用的东西......奇怪。可能是因为 C# 设计者希望您正确捕获特定异常并设计异常层次结构,这样您就不必捕获和重新抛出一些特定派生
  • +1 Earwicker,你的 2cents 真的很棒。我也很想在代码中的某处使用“异常”类。
  • @Lucero - 不一定。尝试处理 AppDomain.UnhandledException,您会发现它会触发 before finally 块运行,让您可以选择在它们执行之前调用 Environment.FailFast。但是任何介入的接球/重掷都会干扰这种能力。
【解决方案5】:

.NET Framework 4.5 现在有一个ExceptionDispatchInfo 支持这种确切的场景。它允许捕获一个完整的异常并从其他地方重新抛出它,而不会覆盖包含的堆栈跟踪。

代码示例应在评论中提出要求

using System.Runtime.ExceptionServices;

class Test
{
    private ExceptionDispatchInfo _exInfo;

    public void DeleteNoThrow(string path)
    {
        try { File.Delete(path); }
        catch(IOException ex)
        {
            // Capture exception (including stack trace) for later rethrow.
            _exInfo = ExceptionDispatchInfo.Capture(ex);
        }
    }

    public Exception GetFailure()
    {
        // You can access the captured exception without rethrowing.
        return _exInfo != null ? _exInfo.SourceException : null;
    }

    public void ThrowIfFailed()
    {
        // This will rethrow the exception including the stack trace of the
        // original DeleteNoThrow call.
        _exInfo.Throw();

        // Contrast with 'throw GetFailure()' which rethrows the exception but
        // overwrites the stack trace to the current caller of ThrowIfFailed.
    }
}

【讨论】:

  • @KonstantinSpirin:认为可用的方法和 MSDN 文档非常明显,但还是添加了一些示例代码以提供概述,而无需单击 MSDN。
  • 这正是我想要的,但遗憾的是我目前的项目不在 4.5 上。但是 +1 让我意识到了这个不错的新功能。
猜你喜欢
  • 2010-11-09
  • 2011-06-01
  • 2014-11-08
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
相关资源
最近更新 更多