为什么不抓住一般例外

时间:2009-11-16 15:36:11

标签: exception

我的VS刚刚告诉我;

  

警告2 CA1031:Microsoft.Design:修改'Program.Main(string [])'以捕获比“异常”更具体的异常或重新抛出异常。

我为什么要这样做?如果我这样做,并没有捕获所有异常来处理它们,我的程序崩溃与所有流行的报告屏幕。我不希望我的用户得到这样的错误废话!

为什么我不能立刻捕捉到所有异常,向用户显示一个不错的警告说:“出了问题,不关心它,我会处理它,只是耐心等待”?

修改:刚看到我在这里有一个骗局,对不起Dupe

Edit2 :澄清事情;在捕获任何异常后我退出程序!我只是不希望我的用户看到“报告到microsoft”对话框,当在控制台应用程序中引发未处理的异常时显示该对话框!

12 个答案:

答案 0 :(得分:38)

吞咽异常是一种危险的做法,因为:

  • 它可以使用户在实际失败时认为某些事情是成功的。
  • 它可以将您的应用程序置于您不计划的状态。
  • 它使调试变得复杂,因为当你处理奇怪/破坏的行为而不是堆栈跟踪时,很难找到失败的地方。

正如您可能想象的那样,其中一些结果可能是非常灾难性的,所以这样做是一个重要的习惯。

最佳实践

首先,防御性地编写代码,以便不会发生超出必要的异常。它们的计算成本很高。

尽可能在粒度级别处理预期的异常(例如:FileNotFoundException)。

对于意外的例外情况,您可以执行以下两项操作之一:

  • 让它们正常冒泡并导致崩溃
  • 抓住他们并优雅地失败

优雅地失败?

假设您正在ASP.Net中工作,并且您不希望向用户显示死亡的黄色屏幕,但您也不希望从开发团队隐藏任何问题。

在我们的应用程序中,我们通常在global.asax中捕获未处理的异常,然后记录并发送通知电子邮件。我们还会显示一个更友好的错误页面,可以使用web.config标记在customErrors中配置。

这是我们的最后一道防线,如果我们最终收到一封电子邮件,我们会马上跳过它。

这种类型的模式与吞咽异常相同,其中你有一个空的Catch块,只存在“假装”没有发生异常。

其他笔记

在VS2010中,会出现一些名为intellitrace的内容,它允许您实际将应用程序状态通过电子邮件发送回家并逐步执行代码,检查异常时的变量值,等等。这将非常有用。

答案 1 :(得分:14)

因为不加区别地(然后继续)吞下(捕获)异常的程序不能依赖于它们应该做的事情。这是因为你不知道什么样的例外被“忽略”了。如果存在溢出或内存访问错误导致从金融帐户中扣除错误金额会怎样?如果它将船驶入冰山而不是远离冰山怎么办?意外故障应始终导致应用程序终止。这迫使开发过程识别并纠正它发现的异常,(演示期间的崩溃是一个很好的激励因素),并且,在生产中,允许适当设计的备份系统在软件遇到“意外”无法做到的时候做出反应旨在做。

编辑:澄清UI组件与服务或中间件组件之间的区别。

在服务或中间件组件中,没有用户在运行代码的同一进程空间内与代码组件交互,组件需要将异常“传递”到任何调用它的客户端组件。正在处理中。无论例外,它都应尽一切可能尝试这样做。但是,在发生意外或意外的异常的情况下,组件应该最终终止其运行的进程。对于预期或预期的异常,应该进行开发分析以确定是否,对于该特定异常,组件及其主机进程可以继续运行(处理将来的请求),或者是否应该终止它。

答案 2 :(得分:7)

您应该处理您能够处理的完全例外,让所有其他人冒泡。如果它向用户显示消息,则表示您不太了解可以处理的内容。

答案 3 :(得分:4)

在使用紧急救援人员使用的设备之后,我宁愿用户看到一条丑陋的错误信息,而不是意外地吞下一个误导用户相信一切都“正常”的异常。根据您的应用,后果可能是从无到有,从失去的销售到灾难性的生命损失。

如果一个人要捕获所有异常,显示一个更好的错误对话框,然后退出应用程序,那没关系..但是如果他们在吞下一个未知异常后继续运行,我会为此解雇一个人。不行。如初。

良好的编码是关于假设人类犯错误的做法。假设所有“关键”异常都被捕获并处理是一个坏主意。

答案 4 :(得分:3)

简单回答:你应该修复你的错误。找到抛出异常的地方,除非它超出您的控制范围 - 修复它。 捕获(不重新抛出)各种异常也违反了异常中立性。一般来说,你不想这样做(虽然在main中捕获异常看起来像特殊情况)

答案 5 :(得分:3)

由于您的警告消息显示这是在Main()中,我将假设在较低级别,您执行仅捕获更具体的例外。

对于Main(),我会考虑两种情况:

  1. 您自己的(调试)版本,您需要获取所有异常信息:不要在此处捕获任何异常,因此调试器会中断并且您有调用堆栈,
  2. 您希望应用程序正常运行的公共版本:Catch Exception并显示一条好消息。这对于普通用户来说总是比“发送报告”窗口更好。
  3. 要做得很好,只需检查是否定义了DEBUG(并定义它,如果VS不自动执行此操作):

    #if DEBUG
      yadda(); // Check only specific Exception types here
    #else
      try
      {
        yadda();
      }
      catch (Exception e)
      {
         ShowMessage(e); // Show friendly message to user
      }
    #endif
    

    我会禁用有关捕获常规异常的警告,但仅针对您的Main()函数,在任何其他方法中捕获异常是不明智的,正如其他海报已经说过的那样。

答案 6 :(得分:2)

suppress certain messages from code analysis有办法。我已经将这个用于这个确切的原因(捕获日志记录的一般例外)并且它非常方便。添加此属性时,它会显示您至少已确认您因特定原因而违反规则。您还会收到不正确的catch块的警告(除了记录之外的其他目的捕获常规异常)。

MSDN SuppressMessageAttribute

答案 7 :(得分:1)

所有答案都很好。但我还要提一个选择。

作者显示一些奇特信息的意图是可以理解的。 另外,默认的Windows错误消息确实很丑陋。此外,如果应用程序未提交给“ Windows卓越计划”,则开发人员将不会收到有关此问题的信息。那么,如果默认运行时处理程序无济于事,那又有什么用呢?

这里的事情是CLR主机(https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/9x0wh2z3(v=vs.90)?redirectedfrom=MSDN)的默认异常处理程序以非常安全的方式工作。这样做的目的很明确:记录错误,将其发送给开发人员,设置过程的返回码并杀死它。更改方法的一般方法是编写自己的主机。在这种情况下,您可以提供自己的异常处理方式。

仍然,有一个简单的解决方案既可以满足CA1031,又可以满足您的大多数需求。

捕获异常时,可以以自己的方式处理(日志,显示消息等),最后可以设置过程结果代码并退出(使用Thread.Abort和“ Exit”的组合)方法)。不过,在捕获块的末尾,您可以只“抛出”; (由于ThreadAbortedException而永远不会调用它,但会满足该规则)。仍然有某些情况,例如StackOverflowException,无法通过这种方式进行处理,您将看到该默认消息框,用于修复您需要后退到自定义CLR主机选项的情况。

此外,仅作为参考,您的应用程序可以运行多个线程(除了一个执行Main方法的线程)。要从所有它们接收异常,可以使用AppDomain.UnhandledException。此事件不允许您将“标记”为已处理的异常,仍然可以使用Thread.Join()冻结线程,然后使用另一个(一个以上)线程来完成该工作(日志,msgbox,退出)。

我了解所有这些看起来有些棘手,可能并不正确,但是我们必须处理AppDomain.UnhandledException,ThreadAbortException,CorruptedState异常和默认CLR主机的实现。所有这些最终并没有给我们太多选择。

答案 8 :(得分:0)

当您捕获一般异常时,您会收到可能隐藏用户运行时问题的副作用,这反过来又会使调试变得复杂。此外,通过捕捉一般异常,你忽略了一个问题(你可能在其他地方抛出)。

答案 9 :(得分:0)

您可以设置try catch以捕获多种不同的行为类型,并根据类型处理异常。对于框架中的大多数方法和属性,您还可以查看它们能够抛出的异常。因此,除非您从极小的代码块中捕获异常,否则您应该捕获特定的异常。

答案 10 :(得分:0)

在VS中,您可以设置自定义错误页面,以便在出现问题时向用户显示,而不是在try-catch中捕获它。我假设你使用VS后你正在使用ASP .NET。如果是这样,请将此标记添加到System.Web标记下的Web.Config:

<customErrors mode="RemoteOnly" defaultRedirect="~/CustomErrorPage.aspx" redirectMode="ResponseRewrite" />

您还可以捕获Global.asax文件中的所有未捕获的异常(如果您还没有它:右键单击Web项目,选择Add Item,然后搜索它)。该文件中有许多应用程序范围的事件处理程序,如“Application_Error”,它捕获应用程序中未捕获的每个异常,因此您不必一直使用Try-Catch。如果发生异常并且可能将它们重定向到您的主页或者某些内容(如果您不想使用上面的customErrors标记),这可以很好地用于向您发送电子邮件。

但最终你不想将整个应用程序包装在try-catch中,也不想捕获一般的异常。如果您捕获每个常规异常,尝试捕获通常会减慢您的应用程序的速度,而不是在几个月或几年之后您不会知道错误的可能性因为try-catch导致您忽略它。

答案 11 :(得分:0)

我全力以赴地捕获特定的已知异常并处理状态...但是我使用常规的catch异常来快速地定位问题,并将错误传递给处理状态的调用方法。在开发过程中被捕获时,它们在一般异常旁边有一个位置,并且在发布时进行了一次处理。

我相信一旦代码投入生产,就应该尝试将其删除,但是在最初的代码创建过程中经常将其困扰。

因此通过Microsoft.CodeQuality.Analyzers中的项目设置关闭(取消选中)警告。在Code Analysis下的项目设置中可以找到:

enter image description here