我一直相信,如果一个方法可以抛出异常,那么不使用有意义的try块来保护这个调用就是鲁莽的。
我刚刚发布了‘你应该总是包装调用,可以抛出try, catch块。,结果被告知这是一个“非常糟糕的建议”——我想知道为什么。
我一直相信,如果一个方法可以抛出异常,那么不使用有意义的try块来保护这个调用就是鲁莽的。
我刚刚发布了‘你应该总是包装调用,可以抛出try, catch块。,结果被告知这是一个“非常糟糕的建议”——我想知道为什么。
当前回答
我的计算机科学教授曾经给我的建议是:“只有在使用标准方法无法处理错误时,才使用Try and Catch块。”
作为一个例子,他告诉我们,如果一个程序在一个地方遇到了一些严重的问题,而不可能做这样的事情:
int f()
{
// Do stuff
if (condition == false)
return -1;
return 0;
}
int condition = f();
if (f != 0)
{
// handle error
}
然后你应该使用try, catch块。虽然您可以使用异常来处理这个问题,但通常不建议这样做,因为异常会消耗大量性能。
其他回答
Herb Sutter在这里写过这个问题。绝对值得一读。 摘要:
"Writing exception-safe code is fundamentally about writing 'try' and 'catch' in the correct places." Discuss. Put bluntly, that statement reflects a fundamental misunderstanding of exception safety. Exceptions are just another form of error reporting, and we certainly know that writing error-safe code is not just about where to check return codes and handle error conditions. Actually, it turns out that exception safety is rarely about writing 'try' and 'catch' -- and the more rarely the better. Also, never forget that exception safety affects a piece of code's design; it is never just an afterthought that can be retrofitted with a few extra catch statements as if for seasoning.
因为下一个问题是“我已经捕获了一个异常,接下来我该做什么?”你会怎么做?如果你什么都不做——这是错误隐藏,程序可能“就是不能工作”,没有任何机会发现发生了什么。您需要了解捕获异常后要做什么,并且只有在知道的情况下才进行捕获。
I was given the "opportunity" to salvage several projects and executives replaced the entire dev team because the app had too many errors and the users were tired of the problems and run-around. These code bases all had centralized error handling at the app level like the top voted answer describes. If that answer is the best practice why didn't it work and allow the previous dev team to resolve issues? Perhaps sometimes it doesn't work? The answers above don't mention how long devs spend fixing single issues. If time to resolve issues is the key metric, instrumenting code with try..catch blocks is a better practice.
How did my team fix the problems without significantly changing the UI? Simple, every method was instrumented with try..catch blocked and everything was logged at the point of failure with the method name, method parameters values concatenated into a string passed in along with the error message, the error message, app name, date, and version. With this information developers can run analytics on the errors to identify the exception that occurs the most! Or the namespace with the highest number of errors. It can also validate that an error that occurs in a module is properly handled and not caused by multiple reasons.
Another pro benefit of this is developers can set one break-point in the error logging method and with one break-point and a single click of the "step out" debug button, they are in the method that failed with full access to the actual objects at the point of failure, conveniently available in the immediate window. It makes it very easy to debug and allows dragging execution back to the start of the method to duplicate the problem to find the exact line. Does centralized exception handling allow a developer to replicate an exception in 30 seconds? No.
语句“方法只有在能够以某种合理的方式处理异常时才应该捕获异常。”这意味着开发人员可以预测或将遇到在发布之前可能发生的每一个错误。如果这是真的,那么应用程序异常处理程序就不需要了,Elastic Search和logstash也就没有市场了。
这种方法还可以让开发人员发现并修复生产中的间歇性问题!是否希望在生产环境中不使用调试器进行调试?或者你宁愿接那些心烦意乱的用户的电话和邮件?这可以让你在其他人知道之前解决问题,而不必通过电子邮件、即时通讯或Slack寻求支持,因为解决问题所需的一切都在那里。95%的问题永远不需要被复制。
为了正常工作,它需要与集中式日志记录相结合,该日志记录可以捕获名称空间/模块、类名、方法、输入和错误消息并存储在数据库中,以便可以聚合它以突出显示哪个方法失败最多,以便首先修复它。
有时候开发人员会选择从catch块向堆栈抛出异常,但这种方法比不抛出异常的普通代码慢100倍。优先使用日志记录进行捕获和释放。
在一家财富500强公司中,该技术被用于快速稳定一款每小时都会出现故障的应用,该应用是由12名开发者历时2年开发的。使用这3000个不同的异常在4个月内被识别、修复、测试和部署。这平均每15分钟修复一次,持续4个月。
我同意,输入所有需要的代码并不有趣,我更喜欢不看重复的代码,但从长远来看,为每个方法添加4行代码是值得的。
如果您想测试每个函数的结果,请使用返回码。
exception的目的是为了降低测试结果的频率。其思想是将异常(不寻常的,罕见的)条件从更普通的代码中分离出来。这使得普通代码更简洁,但仍然能够处理那些异常情况。
在设计良好的代码中,较深的函数可能会抛出,较高级的函数可能会捕获。但关键是,许多“介于两者之间”的功能将完全摆脱处理异常情况的负担。它们只需要是“异常安全的”,这并不意味着它们必须捕获。
除了上面的建议,我个人使用尝试+抓+扔的方法;原因如下:
At boundary of different coder, I use try + catch + throw in the code written by myself, before the exception being thrown to the caller which is written by others, this gives me a chance to know some error condition occured in my code, and this place is much closer to the code which initially throw the exception, the closer, the easier to find the reason. At the boundary of modules, although different module may be written my same person. Learning + Debug purpose, in this case I use catch(...) in C++ and catch(Exception ex) in C#, for C++, the standard library does not throw too many exception, so this case is rare in C++. But common place in C#, C# has a huge library and an mature exception hierarchy, the C# library code throw tons of exception, in theory I(and you) should know every exceptions from the function you called, and know the reason/case why these exception being thrown, and know how to handle them(pass by or catch and handle it in-place)gracefully. Unfortunately in reality it's very hard to know everything about the potential exceptions before I write one line of code. So I catch all and let my code speak aloud by logging(in product environment)/assert dialog(in development environment) when any exception really occurs. By this way I add exception handling code progressively. I know it conflit with good advice but in reality it works for me and I don't know any better way for this problem.