【问题标题】:C# rethrow an exception: how to get the exception stack in the IDE?C#重新抛出异常:如何在IDE中获取异常堆栈?
【发布时间】:2011-05-20 01:50:06
【问题描述】:

这里之前已经讨论过重新抛出异常的正确方法。相反,这个问题是关于如何在使用 rethrow 时从 Visual Studio 中获得有用的行为。

考虑这段代码:

   static void foo() {
        throw new Exception("boo!");
    }

    static void Main(string[] args) {
        try {
            foo();
        } catch (Exception x) {
            // do some stuff
            throw;
        }
    }

出现的异常具有正确的堆栈跟踪,显示 foo() 作为异常的来源。 然而,GUI 调用堆栈窗口只显示 Main,而我希望它显示异常的调用堆栈,一直到 foo。

当没有重新抛出时,我可以使用 GUI 快速导航调用堆栈以查看导致异常的调用以及我们是如何到达那里的。

通过重新抛出,我希望能够做同样的事情。相反,GUI 显示的调用堆栈对我没有用。我必须将异常详细信息复制到剪贴板,将其粘贴到记事本中,然后手动导航到我感兴趣的调用堆栈的任何函数。

顺便说一句,如果我添加 [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] 或将捕获更改为 catch (Exception),我会得到相同的行为。

我的问题是:鉴于我的代码使用了 rethrow,有人可以建议一种方便的方法来导航与异常相关的调用堆栈吗?我正在使用 Visual Studio 2010。

【问题讨论】:

  • 为什么要重新抛出异常? catch 子句中是否还有更多您未在此处包含的代码?否则,我认为您的解决方案是根本不捕获异常。
  • 罗伯特,这是一个最小的 sn-p 来说明问题。实际代码在重新抛出之前做了一些工作。

标签: c# .net visual-studio exception


【解决方案1】:

不是说你应该重新抛出,而是here 的一篇关于如何保留堆栈跟踪的博文,基本上归结为:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

...
catch (Exception ex)
{
  // do something
  // ...
  PreserveStackTrace(ex);
  throw;
}

【讨论】:

  • 谢谢,但这并不能解决我的特殊问题。正如博客文章所解释的那样,此代码有助于在原始异常和捕获位于同一方法中的特殊情况下在异常中获取正确的堆栈跟踪。就我而言,异常包含正确的堆栈跟踪:问题是我不知道如何使用 GUI 来导航该堆栈跟踪。
【解决方案2】:

调试器在Main 中的throw 处中断,因为该异常未处理。默认情况下,调试器只会在未处理的异常上中断。一旦您在Main 停止,来自foo 的原始异常的调用堆栈就会出现在异常中,但所有其他上下文都已丢失(例如本地变量、堆栈/内存状态)。

听起来您希望调试器在 foo 中的 throw 上中断,因此您应该告诉调试器在第一次机会异常时中断:

  1. 调试 » 异常... (Ctrl+Alt+E)
  2. 检查您关心的异常类型的“抛出”(在本例中为 Commange 语言运行时异常)
  3. 点击确定
  4. 开始调试

在这种情况下,当foo 抛出异常时,调试器将立即中断。现在,您可以在原始异常的上下文中检查堆栈、局部变量等。如果您继续执行 (F5),调试器将在Main 重新抛出时再次中断。

采取另一种方法,如果你运行的是VS2010 Ultimate,你还可以使用IntelliTrace“向后调试”来查看异常时的参数、线程和变量。有关详细信息,请参阅this MSDN article。 (完全披露:我在一个与 IntelliTrace 密切相关的团队工作)。

【讨论】:

  • 谢谢,在某些情况下确实可以提供帮助。问题是,就我而言,随着时间的推移会抛出很多异常,而我只对将被重新抛出的少数异常感兴趣。像我现在所做的那样,查看每个抛出的异常将是复制/粘贴调用堆栈的更多工作。我希望有一种简单的方法来导航异常的调用堆栈,即使某些相关状态丢失。
  • @redtuna:请参阅我帖子末尾的编辑(如果您使用的是 VS2010 Ultimate,可能会有用)。另外,抛出的异常是否都属于同一类型?如果没有,您可以在“异常”对话框中指定您不关心的异常类型,然后取消选中“抛出”框以防止调试器中断它们。如果异常都是同一类型,那么是的,这要困难得多。
  • IntelliTrace 看起来令人印象深刻!看起来这是我们使用我这里的工具最接近我们的目标,所以我接受你的回答(尽管 Athari 的回答可能对其他人更好)。
【解决方案3】:

如果您使用ReSharper,您可以将异常堆栈跟踪复制到剪贴板,然后在菜单中选择:ReSharper > 工具 > 浏览堆栈跟踪 (Ctrl+E,T)。它将显示带有可点击位置的堆栈跟踪,因此您将能够快速导航。


(来源:jetbrains.com)

此功能在挖掘用户日志时也非常有用(如果记录了异常的堆栈跟踪)。

【讨论】:

  • 不错!我没有 resharper,但这是一个很棒的功能。听起来这对我来说会加快速度。
  • @redtuna ReSharper 是 C# 开发人员的必备工具。它可以多次加速许多任务。
【解决方案4】:

Mike Stall 已为您的问题提供了great and simple solution:

用属性[DebuggerNonUserCode]标记你重新抛出异常的方法

IDE 会认为这不是您的代码,不会在该位置破坏调试器,而是会在堆栈中进一步查看,显示下一次重新抛出或初始异常位置。

(如果下次重新抛出也很烦,也将其标记为[DebuggerNonUserCode],等等...)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-13
    • 2010-10-10
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 2011-07-12
    • 2012-09-22
    • 2011-06-27
    相关资源
    最近更新 更多