不要在抛出和捕获异常时停止调试器。

99
在工具/异常中,我设置了调试器捕获异常时停止的选项,无论该异常是否被捕获。
我该如何排除这个规则中的一个异常?我的代码中有一个被捕获的异常是程序逻辑的一部分。因此,每次命中它时,我显然不想让该异常停止调试器。
例如:我要忽略第344行上被捕获的空引用异常,并希望在所有其他异常处停止。

6
如果这个异常是你的编程逻辑的一部分(考虑一下,是否真的必须按照这种方式实现)- 那么它应该至少是一个自己创建的、派生的异常。这样你就可以应用Brian的解决方案。 - tanascius
2
@tanascius - +1 我同意在大多数情况下,异常并不是进行逻辑决策的最佳方式;然而,在某些情况下,比如反序列化时,处理异常有时是不可避免的,因此抛出>捕获>处理是唯一合理的选择。 - jpierson
@Will:Visual Studio调试“太局部化”?什么鬼? - Andomar
2
@Ando 抱歉,我的错误。同时调节多个选项卡是有效的,但并不总是准确的。 - user1228
3
在做出响应之前,你可能仍需要先捕捉一个已知的框架异常,才能抛出自己的异常。你的建议并不总是可行的。 - Dan Puzey
显示剩余2条评论
6个回答

71

DebuggerHidden是你的好朋友!

公共语言运行时对该属性没有语义,它是提供给源代码调试器使用的。例如,Visual Studio 2005 调试器不会在标记有此属性的方法中停止,并且不允许在该方法中设置断点。Visual Studio 2005 调试器识别的其他调试器属性包括 DebuggerNonUserCodeAttributeDebuggerStepThroughAttribute

在 VS2010 上测试,并且效果很好。

尽管 DebuggerStepThrough 对某些特定的调试器版本也可以起作用,但根据两个答案的评论,DebuggerHidden似乎适用范围更广。

请注意,两种选项目前均不适用于迭代器块方法异步 / 等待方法。这可能会在 Visual Studio 的后续更新中修复。

请注意,在 .NET Core + Rider 结合的情况下,此功能无法使用,您可以投票支持该问题


2
我在一个方法中添加了该属性,但调试器只停在调用该方法的方法上。我有什么遗漏吗? - Doogal
1
这就是应该的方式。为了避免这种情况,你必须处理异常...或者将调用方法也标记为“DebuggerHidden”... - Shimmy Weitzhandler
1
请注意,DebuggerStepThrough属性足以避免在异常上中断。DebuggerHidden的作用类似于DebuggerNonUserCode和DebuggerStepThrough属性的组合。 - jpierson
1
遗憾的是,如果所讨论的方法是迭代器方法,这种方法就行不通:/ - Roman Starkov
就像Doogal一样,我发现它对我并不起作用。问题在于必须勾选“启用仅限我的代码(仅托管)”,如Valery Letroye所述的答案一样。 - Tony Pulokas
显示剩余2条评论

42

如果我没有记错的话,你可以在包含你不想要异常触发的代码的方法上使用 DebuggerStepThrough 属性。我想你可以将触发烦人异常的代码隔离到一个方法中,并对其进行修饰。


33
根据malinger的回答和我的经验,这个答案似乎是不正确的。 DebuggerStepThrough 属性不会影响调试器对第一次机会异常的行为。 - Michael Petrotta
6
@Tim,我已经测试过了,它无法停止。请看我的回答:https://dev59.com/VXM_5IYBdhLWcg3wUxnF - Shimmy Weitzhandler
6
重要提示:这不适用于异步等待类型的方法。更多信息请参见此处 - i3arnon
8
根据MSDN的说明,DebuggerStepThrough属性对CLR没有意义,它只在调试器中被解释。看起来在各种情况下它并不可靠,在这种情况下,使用DebuggerHidden属性可以可靠地工作。参考链接:https://dev59.com/VXM_5IYBdhLWcg3wUxnF#3455100 - Eric J.
1
DebuggerStepThrough以前对我有用。但在VS 2015中不再起作用了。有人知道如何解决吗? - Matt Fitzmaurice
显示剩余4条评论

14

在其他答案中指定的属性(以及其他属性,例如DebuggerNonUserCode属性)在Visual Studio 2015中默认情况下不再以相同的方式工作。调试器将在标有这些属性的方法中断异常,与旧版本的VS不同。要关闭改变它们行为的性能增强功能,您需要更改注册表设置:

reg add HKCU\Software\Microsoft\VisualStudio\14.0_Config\Debugger\Engine /v AlwaysEnableExceptionCallbacksOutsideMyCode /t REG_DWORD /d 1

更多信息可以在Visual Studio博客上找到。

(这可能应该是置顶答案的评论,但我没有足够的声望)


我们如何重新启用性能增强? - Sarath S Menon
1
我没有测试过,但我想象 reg add HKCU\Software\Microsoft\VisualStudio\14.0_Config\Debugger\Engine /v AlwaysEnableExceptionCallbacksOutsideMyCode /t REG_DWORD /d 0(即将最终值设置为0)将重新启用它。 - bhh

14

DebuggerStepThrough是用于防止在try/catch方法中调试器中断的属性。

但是它只在Visual Studio Debugging选项的General设置(菜单Tools/Options,节点Debugging/General)中未取消选中“启用Just My Code(仅托管代码)”选项时才起作用...

关于该属性的更多信息请参见http://abhijitjana.net/2010/09/22/tips-on-debugging-using-debuggerstepthrough-attribute/

DebuggerHidden仅会防止调试器显示抛出异常的方法。相反,它将显示堆栈上未标记该属性的第一个方法...


1
请注意,默认情况下,此方法在 VS 2015 中不再适用。请参阅 VS 博客了解如何启用它 - bhh
遗憾的是,VS 2015 的解决方法对于 VS 2019 不起作用。 - Jonathan Allen

3
您无法单独指出代码中特定位置抛出的异常。但是,您可以禁用特定类型的异常。
如果您自己的代码抛出所讨论的异常,我建议将其制作为自定义异常,并从适当的异常派生,然后禁用此派生类型上的调试断点。
禁用系统异常,例如NullReferenceException,会影响整个系统,在开发过程中显然不是理想情况。
请注意,异常有两种断点行为:
- 抛出:如果选择此选项,则在抛出此类型的异常时立即断点。 - 用户未处理:如果选择此选项,则仅在try / catch未处理此类型的异常时才断点。
您可以删除“抛出”中对NullReferenceException的检查,这将使您受益于每次系统通过代码中的问题行时都不会中断,但如果系统的其他部分发生未处理的NullReference异常,仍会中断。

3
在Visual Studio 2010中为一个方法添加DebuggerStepThrough属性,可以防止调试器在该方法抛出未处理的异常时停止。 - Tim Murphy
1
我已经测试过了,它没有防止;它仍然停止。 - Shimmy Weitzhandler
1
@Shimmy - 对我来说没问题!请确保在每个方法上从抛出异常的点到您希望异常在调用堆栈中变得可见的点应用DebuggerStepThrough。如果您在所有方法都装饰了DebuggerStepThrough的调用层次结构中捕获并处理异常,您就不应该看到VS在该异常上中断。 - jpierson

2

我在VS 2019中找到了一个解决方案,虽然有点复杂,但对于我的用例来说,解决这个问题是必要的。

操作指南

首先将导致问题的代码移到自己的函数中,这样可以避免在周围代码中抑制异常,并使用DebuggerStepThrough属性(或DebuggerHidden也可以)标记它。

[DebuggerStepThrough]
public void ExceptionThrowingFunction()
{
    //.. Code that throws exception here
}

然后像这样调用它。
OutsourceException(ExceptionThrowingFunction);

那么,您可以选择一个独立的项目(模块),或者是您不关心抑制所有异常的项目,或者是专门为此目的创建的项目。重要的是这个项目必须是单独的,这样您的主项目仍然可以在这种异常类型上中断。在我的情况下,我已经有了一个名为SharedFunctions的小型实用程序项目,所以我把它放在那里。该代码非常稳定,大多数时候不需要调试。

在一个名为ExceptionUtils的类中,我添加了以下函数:

public static T OutsourceException<T>(Func<T> func)
{
    T result = default;
    try
    {
        result = func.Invoke();
    }
    catch { }
    return result;
}

public static T OutsourceException<T, Y>(Func<T, Y> func, Y arg)
{
    T result = default;
    try
    {
        result = func.Invoke(arg);
    }
    catch { }
    return result;
}

public static void OutsourceException<T>(Action<T> action, T arg)
{
    try
    {
        action.Invoke(arg);
    }
    catch { }
}

你可能需要添加更多这样的内容,这些只涵盖了函数中有0-1个参数的基本情况。

最后,当你运行代码时,它仍然会在Outsource函数中出现异常,并显示如下消息:enter image description here。如果你勾选底部的复选框(例如'SharedFunctions.dll'),它将添加一个规则,即如果从该程序集抛出异常,则应忽略该异常。一旦勾选此项,异常应该被抑制。所有其他主程序集中的此类异常仍将像往常一样中断。

这里发生了什么?

由于VS 2015中的更改破坏了[DebuggerHidden]和类似属性(请参见链接到另一个答案的博客文章),因此这些属性现在将异常传递给调用类,调试器只是在那里中断。如果我们将调用类移动到另一个程序集中,就可以使用异常条件系统来抑制异常。

为什么不修复异常?

有些异常无法完全从代码中删除,例如在UWP应用程序中尝试访问Package.Current时。它将始终在调试器中引发异常,这只能由Microsoft解决。在这种情况下的另一种解决方案是将其用#if !DEBUG包围起来,但在其他情况下,这并不实用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接