为什么我不应该在“尝试” – “捕捉”包装每个块?
我一直认为,如果一个方法可以抛出一个exception,那么不要用一个有意义的try块来保护这个调用是鲁莽的。
我只是张贴' 你应该总是打包调用,可以尝试,赶上块。 “ 这个问题 ,并被告知,这是”非常糟糕的build议“ – 我想明白为什么。
一个方法只能在一些合理的方式处理exception的时候才能捕获exception。
否则,把它传递上去,希望调用堆栈的更高的方法可以理解它。
正如其他人所指出的那样,最好在调用堆栈的最高级别有一个未处理的exception处理程序(logging日志),以确保logging任何致命错误。
正如米奇 和 其他人所说,你不应该接受一个你不打算以某种方式处理的例外。 您应该考虑应用程序在devise时如何系统地处理exception。 这通常会导致基于抽象的error handling层,例如,处理数据访问代码中与SQL相关的所有错误,以便与域对象交互的应用程序部分不会暴露于是某个地方的引擎盖下的数据库。
有一些相关的代码气味,除了“抓住一切无处不在”的气味之外,你一定要避免。
-
“catch,log,rethrow” :如果你想基于范围的日志logging,那么编写一个类,当堆栈由于exception而展开(ala
std::uncaught_exception()
)时,在析构函数中发出一个日志语句。 所有你需要做的就是在你感兴趣的范围内声明一个日志logging实例,瞧,你有日志logging,没有不必要的try
/catch
逻辑。 -
“catch,throw translation” :这通常指向一个抽象的问题。 除非你正在实施一个联合的解决scheme,你将几个特定的exception转换为一个更通用的exception,否则你可能有一个不必要的抽象层, 并且不要说“我明天可能需要它” 。
-
“抓,清理,再抛” :这是我的宠儿之一。 如果你看到很多这个,那么你应该应用资源获取是初始化技术,并将清理部分放置在Janitor对象实例的析构函数中。
我认为使用try
/ catch
块的代码是代码审查和重构的一个很好的目标。 这表明无论是exception处理还是不太了解,或者代码已经变成了amba,并且非常需要重构。
因为接下来的问题是“我发现了一个例外,接下来我该怎么办? 你会怎么做? 如果你什么都不做 – 那是隐藏的错误,程序可能“无法正常工作”,没有机会find发生的事情。 你需要明白一旦你发现exception,你会做什么,只有知道的时候才能捕捉到。
香草萨特在这里写了关于这个问题。 肯定值得阅读。
传情:
“编写exception安全的代码,基本上是在正确的地方编写”try“和”catch“。 讨论。
坦率地说,这个声明反映了对exception安全的根本性误解。 exception只是另一种forms的错误报告,我们当然知道编写错误安全代码不仅仅是检查返回代码的位置和处理错误的条件。
事实上,事实certificate,例外安全很less涉及写“尝试”和“抓” – 而且越less越好。 另外,不要忘记,exception安全影响一段代码的devise; 这不仅仅是一种事后的想法,可以用一些额外的声明来加以改进,就好像调味料一样。
你不需要用try-catch来覆盖每个块,因为try-catch仍然可以捕获在调用堆栈下面的函数中抛出的未处理的exception。 所以不是每个函数都有一个try-catch,你可以在应用程序的顶层逻辑中有一个。 例如,可能有一个SaveDocument()
顶级例程,它调用许多调用其他方法的方法等。这些子方法不需要自己的try-catches,因为如果抛出它,它仍然被SaveDocument()
的抓住。
这很好,原因有三:它很方便,因为你有一个地方报告一个错误: SaveDocument()
catch块(s)。 在所有子方法中都不需要重复这个操作,而且这正是您想要的:只有一个地方可以为用户提供有关出错的有用诊断信息。
二,每当抛出一个exception时,保存被取消。 每个子方法都会尝试捕获,如果引发exception,则进入该方法的catch块,执行离开该函数,并通过SaveDocument()
。 如果事情已经出错了,你可能要停下来。
三,你所有的子方法都可以假设每个调用都成功 。 如果一个调用失败,执行将跳转到catch块并且后续代码从不执行。 这可以使你的代码更清洁。 例如,这里有错误代码:
int ret = SaveFirstSection(); if (ret == FAILED) { /* some diagnostic */ return; } ret = SaveSecondSection(); if (ret == FAILED) { /* some diagnostic */ return; } ret = SaveThirdSection(); if (ret == FAILED) { /* some diagnostic */ return; }
以下是如何写入例外:
// these throw if failed, caught in SaveDocument's catch SaveFirstSection(); SaveSecondSection(); SaveThirdSection();
现在发生的事情要清楚得多了。
注意exception安全代码在其他方面编写可能会更复杂:如果引发exception,则不希望泄漏任何内存。 确保你知道RAII ,STL容器,智能指针和其他在析构函数中释放资源的对象,因为在exception之前对象总是被破坏。
正如在其他答案中所述,如果你可以做一些明智的error handling,你应该只捕获一个exception。
例如,在提出问题的问题中,提问者询问将lexical_cast
exception从整数忽略到string是否安全。 这样的演员绝不应该失败。 如果确实失败了,程序中出现了一些错误。 在这种情况下你能做些什么来恢复呢? 最好是让程序死掉,因为它处于一个不可信的状态。 所以不处理exception可能是最安全的事情。
如果你总是在抛出exception的方法的调用者中处理exception,那么exception变得毫无用处,你最好使用错误码。
完全的例外是,它们不需要在呼叫链的每个方法中处理。
我听到的最好的build议是,只有在你能明智地做出某些特殊情况的时候,你才能发现exception情况,而“抓取,login和发布”并不是一个好的策略(如果在图书馆偶尔是不可避免的)。
我的计算机科学教授给我的build议是:“只有在无法使用标准方法处理错误时,才使用Try和Catch块”。
举例来说,他告诉我们,如果一个程序遇到了一个严重的问题,在一个不可能做这样的事情的地方:
int f() { // Do stuff if (condition == false) return -1; return 0; } int condition = f(); if (f != 0) { // handle error }
那么你应该使用try,catch块。 虽然您可以使用exception来处理这个问题,但通常不build议这样做,因为exception是昂贵的性能。
我同意你的问题的基本方向,尽可能在最底层处理尽可能多的例外情况。
一些现有的答案就像“你不需要处理这个exception,其他人会把它堆叠起来”。 根据我的经验,这是一个不好的借口,不要在当前开发的代码块中考虑exception处理,使得exception处理别人或以后的问题。
在分布式开发中,这个问题会急剧增长,您可能需要调用由同事实现的方法。 然后你必须检查一个嵌套的方法调用链来找出他/她为什么要抛出一些exception,这可能在最深的嵌套方法中更容易处理。
如果你想testing每个函数的结果,使用返回代码。
例外的目的是让你可以经常testing结果。 这个想法是从你的更普通的代码中分离出特殊的(罕见的,稀有的)条件。 这使普通的代码更清洁,更简单 – 但仍然能够处理这些特殊情况。
在精心devise的代码中,更深层的函数可能会抛出更高级的函数。 但关键在于“中间”的许多function完全没有处理特殊条件的负担。 他们只需要“exception安全”,这并不意味着他们必须赶上。
除了上面的build议,我个人使用一些try + catch + throw; 原因如下:
- 在不同编码器的边界处,我使用try + catch + throw在自己编写的代码中抛出exception,并将exception抛出给其他人编写的调用者,这让我有机会知道在我的代码中发生了一些错误情况,这个地方更接近最初抛出exception的代码,越接近,越容易find原因。
- 在模块的边界,虽然不同的模块可能会写成我的同一个人。
- 学习+debugging的目的,在这种情况下,我使用catch(…)在C + +中捕获(Exception ex)在C#中,标准库不会抛出太多的exception,所以这种情况在C ++中是罕见的。 但在C#中常见的地方,C#有一个巨大的库和一个成熟的exception层次结构,C#库代码会抛出大量的exception,理论上我(和你)应该知道你调用函数的每个exception,并知道原因/这些exception被抛出,并且知道如何处理它们(优先通过或者抓住并处理它们)。 不幸的是,在写一行代码之前,很难了解潜在的例外情况。 所以我抓住了所有,让我的代码大声说话,通过logging(在产品环境)/断言对话(在开发环境),当真正发生任何exception。 通过这种方式,我逐渐添加exception处理代码。 我知道它很好的build议,但实际上它适用于我,我不知道有什么更好的办法解决这个问题。
如果你想很容易地解决间歇性的生产问题,那么你应该将每一块代码包装在try..catch块中。 这基本上代码的目的是提供广泛的debugging信息,让您无需生产debugging器debugging。 用户不必通过电子邮件或与支持聊天,解决问题所需的所有信息就在那里。 没有必要重现问题。
要正常工作,它需要与广泛的日志logging相结合,可以捕获名称空间/模块,类名称,方法,input和错误消息,并将其存储在数据库中,以便可以聚合以突出显示哪种方法最失败,因此可以先修好
例外是比普通代码慢100到1000倍,不应该重新抛出。 也不要创build一个exception,并抛出它。 这是非常distpruotive。 例外被捕获,所以可以固定的代码。
这项技术被用于快速稳定12个Devs开发超过2年的财富500强公司的一个错误的应用程序。 使用这个我确定,修复,build立testing,并在4个月内部署了3000个修复程序,在这种情况下系统不再报告任何exception,因为所有的处理。 平均每15分钟平均修复4个月。
你不需要在try-catch
里覆盖你的代码的每一部分。 try-catch
块的主要用途是处理错误,并在程序中出现错误/exception。 try-catch
一些用法 –
- 你可以在你想要处理exception的地方使用这个块,或者你可以简单地说这个代码块可能会抛出一个exception。
- 如果要在使用后立即处理对象,可以使用
try-catch
块。