【问题标题】:Is there a benefit to JUST a "throw" in a catch?仅仅“投掷”一次接球有什么好处吗?
【发布时间】:2008-10-20 15:16:01
【问题描述】:

与一位同事就他将大部分功能包装在 try/catch 中的做法进行了“激烈辩论”,但 catch 中只有一个“抛出”,例如

Private sub foo()
    try
        'Do something'
    catch
        throw 'And nothing else!'
    End Try
End Sub

我的想法是不要打扰(假设您此时不需要做任何事情) - 异常会冒泡到父成员中的下一个异常处理程序。

唯一听起来合理的论点是有时没有捕获异常并且您的代码停止(在调试模式下),当前行以绿色突出显示......这可能与多线程有关? 最佳实践确实声明“每个线程都有一个异常处理程序”,但大多数情况下我们都是单线程工作的。

好消息是在调试模式下不会突然弹出给父成员(是的,乔尔!)可能很有用 - 您将转到“抛出”语句并能够检查您的本地成员。 但是你的代码会“到处都是 try/catch/throws”(在这里引用另一个线程)?

如果没有异常发生(即您是否应该避免在紧密循环中尝试/捕获),那么到处添加 try/catch/throws 会涉及什么样的开销?

【问题讨论】:

  • 把你的VB代码整理出来,看起来很可怕。
  • 晚上 11 点尝试用一只手和一个蠕动的婴儿打字! 80/20 - 完成这项工作。
  • 没有提到多线程的好处——我是否认为它不相关?
  • 而且我认为 MSIL 至少会插入一行或 2 行作为 try/catch(MSIL 对我来说是希腊语),所以“不要这样做”的普遍共识是有道理的。跨度>
  • 感谢大家 - 似乎这样做不是最好的,Jon 的 Ctrl+Alt+E 打破所有是正确的(不确定最好的)替代方案。仍然想知道多线程位......但这一切都符合通常的标准实践。

标签: c# .net vb.net


【解决方案1】:

Microsoft 建议在您唯一要做的就是立即重新抛出异常时不要捕获异常(我现在不记得来源了)。 您的代码应该只捕获您想要处理以进行清理或类似操作的异常。

所以通常捕获并重新抛出异常并不是一个好习惯。

捕获并用另一个异常替换它的原因可能是

  • 日志记录
  • 向调用者隐藏敏感信息(堆栈跟踪、异常详细信息)

对于调试,您可能希望更改“异常时中断:”-处理程序(按 Ctrl+Alt+e)在选定的 CLR 异常上“抛出”值。

您可能想了解一下 entlib 异常处理程序块 (EHB),您可以使用它来建立如何处理代码中的异常的模式。

关于你关于性能的问题,我认为在你的代码中有很多 try/catch 块并不是一个问题,但是当你的代码引发并捕获许多异常时,你会遇到性能问题。

【讨论】:

  • 感谢 Ctrl+Alt+e 提示(尽管您需要一个活动的代码窗格)...在主菜单系统中找不到它(我希望它在工具->选项下...调试)。
  • 通常你会在菜单 Debug->Exceptions 下找到它,当项目被加载并且代码窗口处于活动状态时。
  • VS2005 似乎没有那个...但是 Ctrl+Alt+E!?必须升级:-(
  • 不能告诉你,因为我已经卸载了VS2005 :)
【解决方案2】:

在 catch 中单独抛出而不是抛出新异常的原因是因为这会导致原始堆栈跟踪/异常数据被保留。您可能会这样做的一个原因是,您现在可以在此处设置断点以进行调试。

【讨论】:

  • 另一个重新抛出的原因是清理现有状态 - 这就像说,“好吧,我知道发生了不好的事情。我无法处理它,但我会在我把它留给别人之前清理干净。有时 finally 不是适合这个的地方。
  • 虽然单独抛出会保留状态,但根本不会捕获异常。如果要设置断点进行调试,请从 Debug > Exceptions 设置断点。 catch 子句完全没有必要。
  • @DHA:请解释一下您如何在调用堆栈中的精确级别捕获异常,该级别可能有 20 层深。
  • 他的意思是,您应该在给定异常发生时启用自动中断,您可以在调试->异常菜单中启用。
  • 你可以在它被抛出时打破它,但是在适当的数量上回溯堆栈可能会很痛苦。我并不是说想要这样做很常见,但它确实会发生。
【解决方案3】:

我只会在调试问题时这样做 - 我会在签入之前再次删除代码。如果抛出异常,有时可以很方便地设置断点以在特定堆栈级别停止。除此之外 - 没有。

【讨论】:

  • 默认情况下绝对不要这样编码。但是如果我需要在调试会话期间添加它,我可能会保留它。如果我需要它一次,我可能会再次使用它,这是告诉其他开发人员包含的代码可能比看起来更难的一种微妙方式.
  • 我会把它放在评论中,而不是留下丑陋和分散注意力的代码。大多数情况下,当需要阅读该代码时,它可能不会是相同的情况 - 所以对于这种情况不要让它变得更复杂。
  • 我认为测试某些东西不是一个好主意,然后“在签入之前”更改代码。
  • 我不会在签入之前只是这样做。我会使用调试器来找出问题所在;编写失败的单元测试;取出 try/catch/throw 并证明它仍然出错;修复它并看到它全部变绿;提交代码审查;签到。你从不从代码中删除任何东西吗?
  • 追随我心的人,Dour High Arch!现在和一个年轻的联合国大吵一架……任何改变都是新的风险!我觉得有条件的 IF 很有用,但同样危险。
【解决方案4】:

在实践中,我的想法是,如果您不打算处理错误,请不要抓住它。

【讨论】:

    【解决方案5】:

    使用 catch 和立即重新抛出的一个效果是,任何内部的“Finally”块都将在“Catch”发生之前运行(这反过来又会在异常传播之前)。这与两种情况有关:

    1. 如果异常最终未处理,则未处理异常陷阱可能会在不运行任何“finally”块的情况下退出应用程序。执行 catch 并立即重新抛出将确保 catch 中的所有“finally”块都将执行,即使异常最终未处理也是如此。
    2. vb.net 和其他语言中的代码可能会在运行任何 finally 块之前对异常采取行动。使用带有 catch-and-immediate-rethrow 的“try”块将导致该 catch 块中的“finally”块在任何外部“try”块第一次查看异常之前运行。

    另一个关于 catch-and-immediate-rethrow 的警告:由于某种原因,catch and immediate rethrow 将丢弃导致异常的函数调用的堆栈跟踪行号。我不知道为什么在这种情况下不能单独留下堆栈跟踪中的当前函数条目,但事实并非如此。如果不使用 .pdb 文件来获取行号信息,这不是问题,但如果想使用此类信息,可能会很烦人。

    一般来说,上面提到的效果是不可取的,但在某些情况下,前两个效果中的一个或两个可能有用,而第三个效果可以忍受。在这些情况下,立即重新抛出的捕获可能是合适的,但应记录其原因。

    【讨论】:

      【解决方案6】:

      默认情况下总是这样做看起来像是糟糕的设计。但是捕获和抛出可能是有原因的,例如你想抛出一个不同的异常。

      【讨论】:

      • 如果你抛出不同的异常,这不是 OP 描述的情况。他特别谈到了“空投”的情况。
      • 那我不知道是什么原因,因为调试有调试器。
      【解决方案7】:

      您确实不需要需要一个 catch 子句来捕获 Visual Studio 调试器中的异常。选择 Debug > Exceptions,然后选择要捕获的异常,如果需要,选择所有异常。

      【讨论】:

      • 虽然这并不能帮助您在堆栈的特定级别捕获它们,但这通常很方便。
      【解决方案8】:

      如果您捕获一个异常并将其替换为另一个异常,您通常应该将原始异常包装在新异常中。这通常是通过将旧异常传递给新异常的构造函数来完成的。这样,您就可以尽可能多地挖掘以弄清楚发生了什么。不这样做的主要情况是出于安全原因需要隐藏数据。在这些情况下,您应该在清除异常数据之前尝试记录它。

      我看到用新异常包装异常而不是让它们在堆栈中冒泡的基本原理是异常应该与它们来自的方法处于相同的语义级别。如果我调用 AuthenticateUser,我不想看到 SQL 异常。相反,我应该看到一些异常,其名称告诉我身份验证任务无法完成。如果我深入研究这个异常的内部异常,我就可以找到 SQL 异常。就个人而言,我仍在权衡这样做的利弊。

      【讨论】:

        【解决方案9】:

        是的,在 catch 中设置断点很方便。

        另一种更简洁的方法是在您要抛出的对象的构造函数中设置断点。您会在更接近错误源的位置看到程序状态。

        【讨论】:

          【解决方案10】:

          由于零错误处理,这个 catch 是没有用的。如果确实有日志记录或一些清理工作,但在这种情况下,我会摆脱 try/catch。

          【讨论】:

            【解决方案11】:

            如果您需要检查有关异常的某些内容,并为一种情况做某事或在其他情况下抛出它,这也很有用。例如,如果您需要检查 SQLException 中的错误号。如果错误编号是您准备处理的错误编号,您可以执行特定操作。对于其他人,您可以简单地“抛出”它,以便保留堆栈跟踪,如上所述。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-10-12
              • 2011-07-19
              • 2016-05-11
              • 2020-11-05
              • 1970-01-01
              • 2011-02-11
              • 2012-01-22
              • 2023-03-16
              相关资源
              最近更新 更多