为什么优秀的程序员有时会默默吞下exception?
我知道这是邪恶的,但是我已经看到了吞下一个好程序员编写的代码中的exception。 所以我想知道这个不好的做法是否可以有至less一个积极的一面。
换句话说,这很糟糕,但为什么好的程序员在极less数情况下使用它呢?
try { //Some code } catch(Exception){}
看着我自己的代码,我在我的日志代码中发现了一个地方,在写入文件失败并且无法写入事件日志之后,它吞下了错误,因为没有地方可以报告它。 这就是一个例子:其他人不多。
因为有时程序员比编译器有更多的知识。
例如(在一些虚构的语言中,零除被视为错误):
try { x = 1 / (a*a + 1); } catch (DivisionByZeroException ex) { // Cannot happen as a*a+1 is always positive }
由于某些语言(例如Java)需要捕捉一些/许多/所有的exception,编译器可能会抱怨,而程序员知道这是不可能的。 有时让编译器closures的唯一方法是明确地吞下exception。
在编译器没有抱怨的语言中,空的catch通常不会被写入。
编辑:实际上,我会在catch块中添加一个assert(false)。 如果从理论上讲,事情不可能发生,那么在实践中发生这是一个非常严重的问题;)
编辑2:Java只需要捕获检查的exception。 我重申了一下我的发言。
我认为一个好的程序员如果不解释它就不会这么做。
try { //Some code } catch(Exception e) { // Explanation of why the exception is being swallowed. }
我不太可能只是默默吞下基本的Exception
类,不过,出于任何原因。 我至less会尝试logging错误。
下面是我刚刚在我的项目中遇到的一个具体示例。 这是获取浏览器支持的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
它检查要创build的对象的types,则可以安全地吞下该exception。
因为有时甚至好的程序员都会犯错误。
无论是你对一个好的程序员的看法与某些程序员的看法不一样(因为一个好的程序员会留下一些理由,说明为什么Exception被吞噬了,而不是信息)。
我只能想到两种吞噬例外的情况。
-
closures文件或数据库连接时。 如果我收到一个错误,我该怎么办呢? 我想我应该写出一些错误信息,但是如果我已经成功地读取了这些数据,那么看起来就多余了。 这似乎也是一个非常不同的事件发生。
-
更有道理:内部error handling。 如果我试图写入日志文件失败,我会在哪里写错误,说我不能写错误? 在某些情况下,您可以尝试写入屏幕,但取决于应用程序,这可能是不可能的,例如没有屏幕的后台作业; 或只是混淆了用户谁也不能做任何有关的错误。
另外一个海报提到,你知道这个例外是不可能的,或者至less,如果发生这种情况,那么你的编译器或者操作环境就会产生很大的问题,就像没有正确地加上两个数字一样,在这种情况下谁说这个例外是有意义的?
我衷心地同意,在这些罕见的情况下,你应该包括一个评论,以解释为什么这个例外是不相关的或不可能的。 但是,那么我会说,无论何时您编写的代码可能是神秘的,都应该包含解释性的注释。
注意:为什么程序员经常在“x = x + 1; //将1加到x”中join注释,比如说,如果没有注释,我永远都不会把这个注释掉,说明?
我原来的职位后四年增编
当然,为什么即使是好的程序员有时会吞下exception的真正原因是:“我试图获得基本的逻辑工作,如果发生这种exception,我不知道该怎么办,我现在不想弄乱它,我在正常的逻辑stream程中遇到了太多的麻烦,但是稍后我会回头再看。 然后他们忘记回到它。
顺便说一句,我从几位通常认为程序员都认为是好程序员的人那里听到一个不好的理由:“我不想向用户显示一个神秘的错误信息,这只会让他们感到困惑,更好地默默吞下错误。 “ 这是一个不好的理由,因为,(a)你仍然可以写一些东西到一个日志文件。 和(b)一个神秘的错误信息真的比让用户感觉到操作成功的时候更糟吗? 现在,客户认为他们的订单正在处理,或者他们认为救护车正在派遣,以帮助他们的孩子尖叫痛苦,但实际上从来没有消息,即使在最微不足道的情况下在一个论坛上一个平淡无奇的post没有发生,或者这样,我会匹配,而是得到一个神秘的信息,至less给了我一个错误的想法,如果我在意我应该再试一次或打电话给某人,然后只是说“更新完成“,当它不是。
是的,我经常在exception清理代码中使用它们,这样我就不会屏蔽原来的exception。
例如,如果你的catch处理程序试图回滚一个事务并closures一个连接,然后再重新引发这个exception,那么回滚/closures本身可能会失败的原因可能有几个(因为你已经在某些事务中原来的例外)。 所以,可能想用一个空的catch处理程序把clean放到一个try块中,基本上说:“如果清理出了问题,那是好的,因为我们有更大的问题要报告”
因为要完成一个演示文稿,老板会明天自发地做,没有什么比程序崩溃或者显示一个糟糕的错误信息更糟了,而当某些东西不起作用时,他总是可以“谈论”这个东西。
演讲结束后,没有人会改善这些“快速固定”的部分。 😉
我相信这是因为Java检查的exception。 我个人讨厌这个,因为人们往往认为他们需要处理exception的代码。 国际海事组织在99%的代码,你应该只是把你的例外堆栈,而不是处理它。 如果创build的任何新方法的默认签名具有“抛出exception”,那么我将很高兴,这样我就不必处理exception,除非我想。
国际海事组织,你只需要在两个地方的exception处理:1.资源清理,如closuresinputstream,数据库连接或删除文件。 2.在捕捉exception的堆栈顶部,logging或显示给用户。
有些地方你真的不需要在catch中做任何事情,比如从Thread.sleep()处理InterruptedException,那么你至less应该有一个注释来说明你真的不想要发生任何事情那里。
没有自称是一个优秀的程序员,我至less可以解释为什么我有时会忽略和忽视。 有些时候,我正在处理的exception需要处理,但不是实际处理exception的适当位置。 例如,我最近正在开发一个parsing大量XML的程序,作为它的一个任务。 实际的XMLparsing器打开了一个文件,这个文件产生了一个exception。 尽pipe如此,XMLparsing器并不是生成通知用户文件select不正确的对话框的最佳位置。 我并没有把exception扔到正确的地方并在那里处理,而是默默地抓住了这个例外,留下了一个评论和TODO(我的IDE为我追踪)。 这允许我编译和testing,而不用实际处理exception。 有时候,我会立即忘记照顾TODO,吞下的例外会持续一段时间。
我知道这不是最好的编程习惯,我当然不是一个专家程序员,但我认为这是为什么有人会想要这样做的例子。
我已经做了这个,当有一些代码片段,无论失败,我希望程序继续。 例如,我打开了一个可选的日志文件,不pipe为什么它失败了,我想继续执行程序。
尽pipe如此,这不是一个好的代码,因为你可以得到像内存不足这样的exception,而忽略这些exception是毫无意义的,所以即使我已经完成了,它仍然不是最好的主意,我也不会承认这样做。上市。 (糟糕!)
我已经做了一些程序,只要运行一次就可以转换一些数据或者只是closures编译器。 尽pipe如此,这对于任何forms的生产代码都是完全不同的情况。
除了懒惰之外,基本上没有什么好的理由。
我偶然发现了这个旧线程,很惊讶没有看到为什么好的代码只是吞下exception(带或不带注释)的唯一正当理由。
当你使用不在你控制范围内的库和代码编写代码的时候,经常会遇到这样的情况:你需要调用一个方法来获得可能存在或不存在的值,或者执行一个在给定点不允许的操作时间。 如果您正在调用的方法在无法返回有效结果而不是提供控制stream替代方法的情况下会引发exception,那么您将被强制吞下该exception作为值不可用或操作无法执行的信号。
优秀的程序员将尽一切努力避免使用exception处理来驱动控制stream程,除非在真正的故障情况下,这可能会引起用户的错误。 然而,优秀的程序员也广泛地重用库和框架来提高效率,并且理解重写库以提供更好的select(如TryGetValue模式)并不总是一种select。
当exception不能被有效处理时,我们会这样做,它不应该影响应用程序的正常运行,并且/或者它可能代表一个已知的瞬态情况,但总体影响不大。
一个简单的例子就是日志。 你不需要应用程序爆炸,只是因为一些日志logging信息将不会保存到您的支持存储。
另一个例子可能是你有其他方法来处理这种情况,例如:
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块的最后一步。
不幸的是,有很多调用会抛出exception,而不是简单地返回错误代码或错误的结果。 我也看到了一些调用抛出了更多的exception,而不是他们的文档所build议的,这就是为什么我要把所有的东西都放在它们周围的原因之一。
至less把它写到输出窗口
try { } catch (exception ex) { System.Diagnostics.Debug.Writeline(" exception in method so and so : " ex.message); }
如何执行进度控制更新? 我已经更新了控件绘图例程所使用的状态variables,所以下一次绘制控件时会显示正确的状态。 然后我执行:
尝试 MyControl.BeginInvoke(MyControlUpdateDelegate); 赶上例外 结束尝试
如果控件仍然存在,则会更新。 如果它被丢弃,我不会在乎它是否得到更新。 如果控制权没有处理,但由于其他原因无法更新,我仍然不会在意是否更新,因为无论如何我都不能做任何明智的事情。
请注意,我可以添加“如果不是MyControl.IsDisposed那么…”,但这只是将常见的情况下添加工作,并不会防止如果在BeginInvoke中处理控件发生exception。
我需要这样做一次,因为旧的框架在罕见的情况下抛出了无意义的例外,我并不在乎。 我只是需要它在运行的情况下运行。
由于我编写的应用程序的性质,几乎每个我捕获的exception都需要logging到一个文件中。
如果没有任何重新抛出,logging甚至是解释为什么这个例外被无声地吃掉的意见,那么绝对不应该被吃掉。 在我以前的几年里,如果我知道我是唯一一个从事代码工作的人,我会经常吃例外的。 当然,当重新访问代码的时候,我会忘记这个exception的真正意义,我不得不重新testing一切,再次弄明白。
也许是因为它不会被捕获,但无论如何都需要能够。 不过,我想不出为什么要避免某种处理程序。
即使只是因为没有办法做到这一点,仍然很高兴有评论。 但是,在正在进行的具有稳定团队的自定义/垂直应用程序中,如果上述代码中的原因显而易见,则有时候这些细节会消失。
主要是因为他们太累了。 同样的原因,他们忘记做一些时间文件。