C#重新抛出exception:如何在IDE中获取exception堆栈?

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

考虑以下代码:

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

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

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

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

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

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

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

听起来你希望调试器在throw foo中断,所以你应该告诉调试器在第一次机会exception时中断:

  1. 调试»例外…( Ctrl + Alt + E
  2. 检查“Thrown”是否有您关心的exception类型(在本例中为Commange Language Runtime Exceptions)
  3. 单击确定
  4. 开始调试

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

采用另一种方法,如果您正在运行VS2010 Ultimate,您还可以使用IntelliTrace“向后调试”以查看exception时的参数,线程和变量。 有关详细信息,请参阅此MSDN文章 。 (完全披露:我在与IntelliTrace密切相关的团队工作)。

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

http://sofzh.miximages.com/c%23/Reference__Windows__Stack_Trace_Explorer.png

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

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

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

Mike Stall为您的问题提供了一个非常简单的解决方案 :

使用属性[DebuggerNonUserCode]标记重新抛出exception的方法

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

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