在使用 FXCop 分析一些遗留代码时,我想到在 try 块中捕获一般异常错误真的那么糟糕,还是应该寻找特定的异常。请考虑一下明信片。

有帮助吗?

解决方案

显然,这是唯一真正答案是“视情况而定”的问题之一。

它主要取决于你在哪里捕获异常。一般来说,库在捕获异常方面应该更加保守,而在程序的顶层(例如在您的主要方法中或在控制器中的操作方法的顶部等)您可以更自由地对待您所捕获的内容。

其原因是例如您不想捕获库中的所有异常,因为您可能会掩盖与您的库无关的问题,例如“OutOfMemoryException”,您确实希望它冒泡以便通知用户等。另一方面,如果您正在讨论在 main() 方法中捕获异常,该方法捕获异常,显示它然后退出......好吧,在这里捕获任何异常可能都是安全的。

关于捕获所有异常的最重要规则是,您永远不应该默默地吞下所有异常......例如Java 中是这样的:

try { 
    something(); 
} catch (Exception ex) {}

或者Python中的这个:

try:
    something()
except:
    pass

因为这些可能是一些最难追踪的问题。

一个好的经验法则是,您应该只捕获您自己可以正确处理的异常。如果你不能完全处理异常,那么你应该让它冒泡给可以处理的人。

其他提示

除非您在应用程序的前端进行一些日志记录和清理代码,否则我认为捕获所有异常是不好的。

我的基本经验法则是捕获您期望的所有异常,其他任何内容都是错误。

如果你抓住了一切并继续前进,这有点像在汽车仪表板上的警告灯上贴了一块膏药。你再也看不到它了,但这并不意味着一切都好。

是的!(应用程序的“顶部”除外)

通过捕获异常并允许代码继续执行,您就表明您知道如何处理和规避或修复特定问题。你是说这是 可恢复的情况. 。捕获 Exception 或 SystemException 意味着您将捕获 IO 错误、网络错误、内存不足错误、缺失代码错误、空指针取消引用等问题。说你能处理这些是骗人的。

在组织良好的应用程序中,这些不可恢复的问题应该在堆栈的高层处理。

此外,随着代码的发展,您不希望您的函数捕获添加的新异常 将来 到被调用的方法。

在我看来,你应该捕获所有异常 预计, ,但此规则适用于除界面逻辑之外的任何内容。在调用堆栈的整个过程中,您可能应该创建一种方法来捕获所有异常,进行一些日志记录/提供用户反馈,并且如果需要且可能的话,优雅地关闭。

没有什么比应用程序因一些用户不友好的堆栈跟踪转储到屏幕而崩溃更糟糕的了。它不仅可以(也许是不需要的)深入了解您的代码,而且还会使您的最终用户感到困惑,有时甚至将他们吓到竞争的应用程序。

关于这个问题有很多哲学讨论(更像是争论)。就我个人而言,我认为你能做的最糟糕的事情就是吞下异常。下一个最糟糕的情况是允许异常浮出表面,用户看到的屏幕上充满了技术性的胡言乱语。

我认为这一点是双重的。

首先,如果您不知道发生了什么异常,您如何希望从中恢复。如果您预计用户可能会输入错误的文件名,那么您可能会出现 FileNotFoundException 并告诉用户重试。如果相同的代码生成 NullReferenceException 并且您只是告诉用户重试,他们将不知道发生了什么。

其次,FxCop 指南确实关注库/框架代码 - 并非所有规则都设计为适用于 EXE 或 ASP.Net 网站。因此,拥有一个全局异常处理程序来记录所有异常并很好地退出应用程序是一件好事。

捕获所有异常的问题在于,您可能会捕获您不希望捕获的异常,或者确实捕获您应该捕获的异常 不是 抓住。事实上,任何类型的异常都表明出现了问题,您必须在继续之前解决它,否则您可能会遇到数据完整性问题和其他不容易追踪的错误。

举一个例子,在一个项目中我实现了一种名为 CriticalException 的异常类型。这表明存在错误情况,需要开发人员和/或管理人员进行干预,否则客户会收到错误的账单,或者可能导致其他数据完整性问题。它还可以用于其他类似的情况,即仅记录异常是不够的,需要发送电子邮件警报。

另一位没有正确理解异常概念的开发人员随后将一些可能会引发此异常的代码包装在通用的 try...catch 块中,该块丢弃了所有异常。幸运的是,我发现了它,但它可能会导致严重的问题,特别是因为它应该捕获的“非常不常见”的极端情况结果比我预期的要常见得多。

所以一般来说,捕获通用异常是不好的,除非你 100% 确定你知道 确切地 什么样的异常会被抛出,在什么情况下会被抛出。如果有疑问,让它们冒泡到顶级异常处理程序。

这里的类似规则是永远不要抛出 System.Exception 类型的异常。您(或其他开发人员)可能希望捕获调用堆栈上方的特定异常,同时让其他人通过。

(不过,有一点需要注意。在 .NET 2.0 中,如果线程遇到任何未捕获的异常,它将卸载整个应用程序域。因此,您应该将线程的主体包装在通用的 try...catch 块中,并将在那里捕获的任何异常传递给全局异常处理代码。)

好吧,我看不出捕获一般异常或特定异常之间有什么区别,除了当有多个 catch 块时,您可以根据异常的情况做出不同的反应。

总之,您将同时获得 IOExceptionNullPointerException 与通用的 Exception, ,但是你的程序应该做出反应的方式可能是不同的。

我想扮演魔鬼的代言人,捕捉异常并记录它并重新抛出它。例如,如果您在代码中的某个位置发生意外异常,您可以捕获它,记录在简单堆栈跟踪中无法获得的有意义的状态信息,然后将其重新抛出到上层以处理。

有两种完全不同的用例。第一个是大多数人正在考虑的,在一些需要检查异常的操作周围放置一个 try/catch。无论如何,这不应该是包罗万象的。

然而,第二个是阻止程序在可以继续运行时中断。这些案例是:

  • 所有线程的顶部(默认情况下,异常将消失得无影无踪!)
  • 在您希望永远不会退出的主处理循环中
  • 在循环内处理一系列对象,其中一个失败不应阻止其他对象
  • “主”线程的顶部——您可以在这里控制崩溃,例如当内存不足时将一些数据转储到标准输出。
  • 如果您有一个运行代码的“Runner”(例如,如果有人向您添加侦听器并且您调用侦听器),那么当您运行代码时,您应该捕获异常以记录问题并让您继续通知其他侦听器。

在这些情况下,您总是希望捕获异常(有时甚至可能是可抛出的),以便捕获编程/意外错误,记录它们并继续。

我认为一个好的指导方针是仅捕获框架内的特定异常(以便主机应用程序可以处理磁盘填满等边缘情况),但我不明白为什么我们不能捕获所有异常我们的应用程序代码中的异常。很简单,有时您不希望应用程序崩溃,无论出现什么问题。

大多数时候不需要捕获一般异常。当然,有些情况下你别无选择,但在这种情况下,我认为最好检查一下为什么你需要抓住它。也许你的设计有问题。

捕获一般异常,我感觉就像在燃烧的建筑物内拿着一根炸药,然后扑灭引信。它会在短时间内有所帮助,但一段时间后炸药无论如何都会爆炸。

当然,在某些情况下,可能需要捕获一般异常,但仅用于调试目的。错误和错误应该被修复,而不是隐藏。

对于我的 IabManager 类,我将其用于应用程序内计费(来自在线的 TrivialDrive 示例),我注意到有时我会处理很多异常。已经到了难以预料的地步了。

我意识到,只要我在发生一个异常后停止尝试消费应用内产品(这是大多数异常发生的地方(在消费中,而不是在购买中)),我就会安全。

我只是将所有异常更改为一般异常,现在我不必担心抛出任何其他随机的、不可预测的异常。

前:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

后:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

不受欢迎的意见:并不真地。

捕获所有可以有效恢复的错误。有时这就是全部。

根据我的经验,这更重要 在哪里 异常来自于实际抛出的异常。如果你把例外情况限制在狭小的范围内,你通常不会吞下任何本来有用的东西。错误类型中编码的大多数信息都是辅助信息,因此您通常最终会有效地捕获 全部 无论如何(但您现在必须查找 API 文档以获取可能的异常的全部集合)。

请记住,一些异常在几乎所有情况下都应该冒泡到顶部,例如 Python 的 KeyboardInterruptSystemExit. 。对于 Python 来说幸运的是,这些都保存在异常层次结构的单独分支中,因此您可以通过捕获来让它们冒泡 Exception. 。精心设计的异常层次结构使此类事情变得非常简单。

捕获一般异常会导致严重问题的主要时间是在处理需要清理的资源时(也许在一个 finally 子句),因为包罗万象的处理程序很容易错过这类事情。幸运的是,这对于具有以下语言的语言来说并不是真正的问题 defer, ,像 Python 一样构造 with, ,或 C++ 和 Rust 中的 RAII。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top