42

我知道这是邪恶的,但我已经看到一个优秀程序员编写的代码中吞下了异常。所以我想知道这种不良做法是否至少有一个积极意义。

换句话说,它很糟糕,但为什么优秀的程序员在极少数情况下会使用它?

try
{
    //Some code
}
catch(Exception){}
4

16 回答 16

49

查看我自己的代码,我在日志记录代码中找到了一个位置,在无法写入文件并且无法写入事件日志之后,它会吞下错误,因为没有地方可以报告它。这是一个例子:没有很多其他的。

于 2010-07-26T13:49:56.207 回答
26

因为有时程序员比编译器有更多的知识。

例如(在某些虚构语言中,除以零被视为错误):

try {
    x = 1 / (a*a + 1);
} catch (DivisionByZeroException ex) {
  // Cannot happen as a*a+1 is always positive
}

由于某些语言(例如Java)需要捕获一些/许多/所有异常,编译器可能会抱怨,而程序员知道这是不可能的。有时让编译器关闭的唯一方法是明确地吞下异常。

在编译器不抱怨的语言中,通常根本不编写空捕获。

编辑:实际上,我会在 catch 块中添加一个 assert(false) 。如果理论上某些事情不会发生,那么当它在实践中发生时,这是一个非常严重的问题;)

Edit2:Java 只需要捕获已检查的异常。稍微改写了我的陈述。

于 2010-07-26T14:16:02.677 回答
21

我认为一个好的程序员不会在不解释的情况下这样做。

try
{
    //Some code
}
catch(Exception e) {
    // Explanation of why the exception is being swallowed.
}

Exception不过,出于任何原因,我不太可能只是默默地吞下基类。我至少会尝试记录错误。


这是我今天在我的项目中碰巧遇到的一个具体示例。XMLHttpRequest这是浏览器支持的 JavaScript 函数的一部分。

var XMLHttp = null;

if( window.XMLHttpRequest ) {
    try {
        XMLHttp = new XMLHttpRequest();
    } catch(e) {} // we've already checked that this is safe. 
}
else if( window.ActiveXObject ) {
...
}

由于try包裹在if...else if检查要创建的对象类型的 an 中,因此可以安全地吞下异常。

于 2010-07-26T13:51:26.283 回答
16

我只能想到两种我吞下异常的情况。

  1. 关闭文件或数据库连接时。如果我在收盘时遇到错误,我该怎么办?我想我真的应该写出某种错误信息,但如果我已经成功读取数据,这似乎是多余的。这似乎也是一个非常不同的事件发生。

  2. 更合理:内部错误处理。如果我尝试写入日志文件失败,我将在哪里写入表明我无法写入错误的错误?在某些情况下,您可以尝试写入屏幕,但取决于应用程序,这可能是不可能的,例如没有屏幕的后台作业;或者只是让那些无论如何都无法对错误做任何事情的用户感到困惑。

另一张海报提到了您知道异常是不可能的情况,或者至少,要发生异常,您的编译器或操作环境必须存在重大问题,例如它没有正确地将两个数字加在一起,在这种情况下谁说例外有意义吗?

我衷心同意,在这些罕见的合法情况下,您应该添加注释来解释为什么例外是不相关或不可能的。但是,我想说,任何时候你编写的代码可能很神秘,你都应该包含一个解释性的注释。

旁注:为什么程序员通常会包含诸如“x = x + 1;//将1添加到x”之类的注释,就像duh一样,如果没有注释,我永远不会想到这一点,但是然后抛出了真正神秘的代码而没有解释?

我的原始帖子四年后的附录

当然,即使是优秀的程序员有时也会接受异常的真正原因是:“我正在努力让基本逻辑正常工作,如果发生这种异常我不知道该怎么办,我现在不想搞砸它,我在正常的逻辑流程上遇到了太多麻烦,但我稍后会回复。” 然后他们忘记回到它。

顺便说一句,我从几个我通常认为是非常优秀的程序员的人那里听到的一个不好的原因是:“我不想向用户显示一个神秘的错误消息。那只会让他们感到困惑。最好默默地接受错误。 " 这是一个不好的理由,因为 (a) 您仍然可以将某些内容写入日志文件。并且 (b) 一个神秘的错误信息真的比让用户认为操作成功而没有成功的印象更糟糕吗?现在客户认为他们的订单正在处理,而实际上并非如此,或者他们认为救护车正在被派去帮助他们的孩子,他们在痛苦中尖叫但实际上消息从未通过,等等。即使在最微不足道的情况下,论坛上没有出现平淡无奇的帖子之类的,

于 2010-07-26T17:11:40.770 回答
9

因为有时即使是优秀的程序员也会犯错误。

无论是那个还是你对一个优秀程序员的看法都与某些不同(因为一个优秀的程序员会留下一些关于为什么吞下异常而不是对信息进行处理的推理)。

于 2010-07-26T13:49:13.723 回答
7

因为必须为演示文稿完成某些事情,所以老板明天自发地做,没有什么比程序崩溃或显示令人讨厌的错误消息更糟糕的了,而当某些事情不工作时,他总是可以“谈论”那个。

演示结束后,当然没有人会改进那些“快速修复”的部分。;-)

于 2010-07-27T07:51:19.270 回答
6

我相信这是因为 Java 的检查异常。我个人因此讨厌他们,因为人们倾向于认为他们在任何地方都需要异常处理代码。IMO 在 99% 的代码中,您应该只是将异常抛出堆栈而不是处理它。如果创建的任何新方法的默认签名中包含“抛出异常”,我会很高兴,这样我就不必处理异常,除非我愿意。

IMO 您只需要在两个地方进行异常处理: 1. 用于资源清理,例如关闭输入流、数据库连接或删除文件。2. 在堆栈的最顶端捕获异常并将其记录或显示给用户。

有些地方你真的不需要在 catch 中做任何事情,比如处理来自 Thread.sleep() 的 InterruptedException 并且你至少应该有一个评论来明确你真的不希望任何事情发生那里。

于 2010-10-22T12:54:57.670 回答
4

是的,我经常在异常清理代码中使用它们,这样我就不会掩盖原始异常。

例如,如果您的 catch 处理程序在重新处理异常之前尝试回滚事务并关闭连接,则回滚/关闭本身可能失败的原因可能有多种(因为您已经因为原始例外)。所以,可能想用空的 catch 处理程序将 clean 包装在 try 块中,基本上是说“如果清理中出现问题,那没关系,因为我们有更大的问题要报告”

于 2010-07-26T13:53:15.250 回答
2

当无法有效处理异常时,我会这样做,它不应该影响应用程序的正常运行,和/或它可能代表已知但总体影响很小的瞬态条件。

一个简单的例子是记录。您不需要因为某些日志记录信息不会保存到您的后备存储而导致应用程序崩溃。

另一个示例可能是您有其他处理情况的方法,如下所示:

private Boolean SomeMethod() {
   Boolean isSuccessful = false;
   try {
      // call API function that throws one of several exceptions on failure.
      isSuccessful = true;
   } catch(Exception) { }

   return isSuccessful;
}

在上面的示例中,您可能没有后备位置,但您不希望它被过滤掉。这对于 SHORT 方法是可以的,其中返回值仅设置为成功状态,作为 try 块中的最后一步。

不幸的是,有许多调用会抛出异常,而不是简单地在结果中返回错误代码或错误条件。我还看到一些调用引发的异常比他们的文档建议的要多,这就是为什么我会在它们周围放置一个捕获的原因之一。

于 2010-07-26T14:07:11.203 回答
2

我偶然发现了这个旧线程,并惊讶于没有看到好的代码会简单地吞下异常(有或没有注释)的唯一合理原因。

当您使用超出您控制范围的库和代码编写代码时,经常会出现以下情况:您需要调用方法来获取可能存在或不存在的某些值,或者执行在给定点可能不允许的操作时间。如果您正在调用的方法在无法返回有效结果而不是提供控制流替代方案的情况下引发异常,那么您将被迫将异常作为值不可用或操作无法执行的信号吞下。

优秀的程序员将尽一切努力避免使用异常处理来驱动控制流,除非在可能作为错误冒泡给用户的实际故障场景中。然而,优秀的程序员也会广泛地重用库和框架以提高效率,并且理解重写库以提供更好的替代方案(如 TryGetValue 模式)并不总是一种选择。

于 2012-01-10T19:52:31.830 回答
1

在不自称是一名优秀的程序员的情况下,我至少可以解释为什么我有时会捕捉和忽略。有时我正在处理的工作必须处理要编译的异常,但不是实际处理异常的合适位置。例如,我最近在一个程序中工作,该程序将解析大量 XML 作为其任务之一。实际的 XML 解析器打开了一个文件,该文件生成了一个异常。不过,XML 解析器并不是生成对话框通知用户错误文件选择的最佳位置。我没有将异常抛出到正确的位置并在那里处理它,而是默默地捕捉到异常,留下评论和 TODO(我的 IDE 为我跟踪)。这使我能够在不实际处理异常的情况下进行编译和测试。有时,我会立即忘记处理 TODO,

我知道这不是最好的编程实践,我当然不是专家级的程序员,但我认为这是为什么有人想要这样做的一个例子。

于 2010-07-26T14:12:10.697 回答
1

当有一些代码不管它失败我希望程序继续运行时,我已经这样做了。例如,我打开了一个可选的日志文件,无论它为什么无法工作,我都想继续执行程序。

即使这样,它也不是好的代码,因为你可以得到像内存不足异常这样的异常,而忽略这些异常是没有意义的,所以即使我已经做到了,它仍然不是最好的主意,我不会承认这样做上市。(哎呀……)

我已经在打算运行一次以转换一些数据或只是为了关闭编译器的程序上完成了它。不过,这与任何形式的生产代码完全不同。

除了懒惰之外,基本上没有什么好的理由这样做。

于 2010-07-26T14:58:04.123 回答
1

我们对运行时错误使用异常。因为用户有时要求之类的事情发生的时候就会出现问题。如果我们想向用户显示错误,我们可能会引发异常。例如:-

  1. 软件运行时可能会出现需求错误,可以通过程序自行解决。所以在这种情况下,程序员选择使用try语句,并且可能存在软件无法解决的硬件要求,在这种情况下程序员选择引发异常的路径来提醒用户。

并且在很多情况下,我们会选择引发异常或处理异常情况的路径。

于 2019-08-03T02:01:32.353 回答
0

执行进度控制更新时怎么样?我已经更新了控件绘图例程使用的状态变量,因此下次绘制控件时,它将显示正确的状态。然后我执行:

  尝试
    MyControl.BeginInvoke(MyControlUpdateDelegate);
  将 Ex 捕获为异常
  结束尝试

如果控件仍然存在,它将更新。如果它被处理掉,我不在乎它是否被更新。如果控件没有被释放,但由于其他原因无法更新,我仍然不会真正关心它是否被更新,因为无论如何我都不能做任何明智的事情。

请注意,我可以添加“If Not MyControl.IsDisposed Then...”,但这只会在常见情况下添加工作,并且如果控件在 BeginInvoke 期间被释放,则不会阻止发生异常。

于 2010-07-26T15:21:24.670 回答
0

由于我编写的应用程序的性质,几乎我捕获的每个异常都需要记录到文件中。

如果没有任何类型的重新抛出、日志记录,甚至是解释为什么异常被静默吃掉的注释,就永远不应该吃掉异常。在我早年,如果我知道我是唯一一个编写代码的人,我经常会吃异常。当然,当需要重新访问代码时,我会忘记该异常的真正含义,并且我必须重新测试所有内容才能再次弄清楚。

于 2010-07-26T15:32:15.993 回答
0

至少将其写入输出窗口

try
{ }
catch (exception ex)
{
System.Diagnostics.Debug.Writeline(" exception in method so and so : " ex.message);
}
于 2010-07-26T14:23:04.667 回答