之前已经讨论过重新抛出异常的正确方法。相反,这个问题是关于如何在使用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)
,我会得到相同的行为。
我的问题是:鉴于我使用的代码使用了重新抛出,有人可以建议一种方便的方法来导航与异常相关的调用堆栈吗?我正在使用Visual Studio 2010。
答案 0 :(得分:11)
调试器在throw
的{{1}}处中断,因为该异常未处理。默认情况下,调试器只会中断未处理的异常。一旦你停在Main
,来自Main
的原始异常的调用堆栈就会出现在异常中,但所有其他上下文都已丢失(例如本地,堆栈/内存状态)。
听起来您希望调试器在foo
中的throw
中断,因此您应该告诉调试器在第一次机会异常时中断:
在这种情况下,当foo
抛出异常时,调试器将立即中断。现在,您可以在原始异常的上下文中检查堆栈,本地等。如果继续执行( F5 ),调试器将在foo
的重新抛出中再次中断。
采用另一种方法,如果您正在运行VS2010 Ultimate,您还可以使用IntelliTrace“向后调试”以查看异常时的参数,线程和变量。有关详细信息,请参阅this MSDN article。 (完全披露:我在与IntelliTrace密切相关的团队工作)。
答案 1 :(得分:8)
如果使用ReSharper,可以将异常堆栈跟踪复制到剪贴板,然后在菜单中选择: ReSharper>工具>浏览堆栈跟踪(Ctrl + E,T)。它将显示具有可点击位置的堆栈跟踪,因此您将能够快速导航。
http://www.jetbrains.com/resharper/webhelp/images/Reference__Windows__Stack_Trace_Explorer.png
此功能在挖掘用户日志时非常有用(如果记录了异常堆栈跟踪)。
答案 2 :(得分: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;
}
答案 3 :(得分:0)
Mike Stall给你的问题great and simple solution:
使用属性[DebuggerNonUserCode]
IDE会认为这不是您的代码,不会在这样的地方破坏调试器,而是会在堆栈中进一步查看,显示下一个重新抛出或初始异常位置。
(如果下一次重新抛出也很烦人,也将其标记为[DebuggerNonUserCode]
等等......)