为什么要保守地使用例外?
我经常看到/听到人们说应该很少使用异常,但从不解释为什么.虽然这可能是真的,但基本原理通常是一个花言巧语:它被称为异常是有原因的",对我来说,这似乎是一种受人尊敬的程序员永远不应该接受的解释/工程师.
I often see/hear people say that exceptions should only be used rarely, but never explain why. While that may be true, rationale is normally a glib: "it's called an exception for a reason" which, to me, seems to be the sort of explanation that should never be accepted by a respectable programmer/engineer.
可以使用异常来解决一系列问题.为什么将它们用于控制流是不明智的?在如何使用它们时格外保守背后的哲学是什么?语义?表现?复杂?美学?约定?
There is a range of problems that an exception can be used to solve. Why is it unwise to use them for control flow? What is the philosophy behind being exceptionally conservative with how they are used? Semantics? Performance? Complexity? Aesthetics? Convention?
我之前看过一些关于性能的分析,但分析的水平与某些系统相关而与其他系统无关.
I've seen some analysis on performance before, but at a level that would be relevant to some systems and irrelevant to others.
同样,我不一定不同意他们应该在特殊情况下保存,但我想知道共识的基本原理是什么(如果存在这样的事情).
Again, I don't necessarily disagree that they should be saved for special circumstances, but I'm wondering what the consensus rationale is (if such a thing exists).
推荐答案
主要的摩擦点是语义.许多开发人员滥用异常并一有机会就抛出异常.这个想法是在一些特殊情况下使用异常.例如,错误的用户输入不算作异常,因为您希望这会发生并为此做好准备.但是,如果您尝试创建文件并且磁盘上没有足够的空间,那么是的,这绝对是一个例外.
The primary point of friction is semantics. Many developers abuse exceptions and throw them at every opportunity. The idea is to use exception for somewhat exceptional situation. For example, wrong user input does not count as an exception because you expect this to happen and ready for that. But if you tried to create a file and there was not enough space on disk, then yes, this is a definite exception.
另一个问题是异常经常被抛出和吞噬.开发人员使用这种技术来简单地静默"程序并让它尽可能长时间地运行直到完全崩溃.这是非常错误的.如果你不处理异常,如果你没有通过释放一些资源做出适当的反应,如果你没有记录异常的发生或者至少没有通知用户,那么你就没有使用异常来表达它们的意思.
One other issue is that exceptions are often thrown and swallowed. Developers use this technique to simply "silence" the program and let it run as long as possible until completely collapsing. This is very wrong. If you don't process exceptions, if you don't react appropriately by freeing some resources, if you don't log the exception occurrence or at least not notify the user, then you're not using exception for what they are meant.
直接回答您的问题.应该很少使用异常,因为异常情况很少见,而且异常代价高昂.
Answering directly your question. Exceptions should rarely be used because exceptional situations are rare and exceptions are expensive.
很少见,因为您不会期望程序在每次按下按钮或每次格式错误的用户输入时都会崩溃.比如说,数据库可能突然无法访问,磁盘空间不足,您依赖的某些第三方服务离线,这一切都可能发生,但很少发生,这些都是明显的例外情况.
Rare, because you don't expect your program crash at every button press or at every malformed user input. Say, database may suddenly not be accessible, there may not be enough space on disk, some third party service you depend on is offline, this all can happen, but quite rarely, these would be clear exceptional cases.
昂贵,因为抛出异常会中断正常的程序流程.运行时将展开堆栈,直到找到可以处理异常的适当异常处理程序.它还将一路收集调用信息以传递给处理程序将接收的异常对象.这一切都有成本.
Expensive, because throwing an exception will interrupt the normal program flow. The runtime will unwind the stack until it finds an appropriate exception handler that can handle the exception. It will also gather the call information all along the way to be passed to the exception object the handler will receive. It all has costs.
这并不是说使用异常就没有例外(微笑).有时,如果您抛出异常而不是通过多层转发返回码,它可以简化代码结构.作为一个简单的规则,如果您希望某个方法经常被调用并在一半的时间内发现一些异常"情况,那么最好找到另一个解决方案.但是,如果您希望大部分时间操作流程正常,而这种异常"情况只会在极少数情况下出现,那么抛出异常就可以了.
This is not to say that there can be no exception to using exceptions (smile). Sometimes it can simplify the code structure if you throw an exception instead of forwarding return codes via many layers. As a simple rule, if you expect some method to be called often and discover some "exceptional" situation half the time then it is better to find another solution. If however you expect normal flow of operation most of the time while this "exceptional" situation can only emerge in some rare circumstances, then it is just fine to throw an exception.
@Comments: Exception 绝对可以用在一些不太异常的情况下,如果这可以使你的代码更简单和更容易的话.这个选项是开放的,但我想说它在实践中很少见.
@Comments: Exception can definitely be used in some less-exceptional situations if that could make your code simpler and easier. This option is open but I'd say it comes quite rare in practice.
为什么将它们用于控制流是不明智的?
Why is it unwise to use them for control flow?
因为异常会破坏正常的控制流".您引发异常并且程序的正常执行被放弃,这可能会使对象处于不一致的状态并且一些开放的资源未被释放.当然,C# 有 using 语句,即使使用主体抛出异常,也可以确保对象将被释放.但是让我们暂时从语言中抽象出来.假设框架不会为您处理对象.你手动完成.你有一些关于如何请求和释放资源和内存的系统.您在系统范围内达成一致,谁负责在什么情况下释放对象和资源.您有如何处理外部库的规则.如果程序遵循正常的操作流程,效果很好.但是突然在执行过程中你抛出了一个异常.一半的资源未被释放.一半还没有被请求.如果该操作现在是事务性的,那么它就被破坏了.您处理资源的规则将不起作用,因为负责释放资源的那些代码部分根本不会执行.如果其他人想使用这些资源,他们可能会发现它们处于不一致的状态并崩溃,因为他们无法预测这种特殊情况.
Because exceptions disrupt normal "control flow". You raise an exception and normal execution of the program is abandoned potentially leaving objects in inconsistent state and some open resources unfreed. Sure, C# has the using statement which will make sure the object will be disposed even if an exception is thrown from the using body. But let us abstract for the moment from the language. Suppose the framework won't dispose objects for you. You do it manually. You have some system for how to request and free resources and memory. You have agreement system-wide who is responsible for freeing objects and resources in what situations. You have rules how to deal with external libraries. It works great if the program follows the normal operation flow. But suddenly in the middle of execution you throw an exception. Half of the resources are left unfreed. Half have not been requested yet. If the operation was meant to be transactional now it is broken. Your rules for handling resources will not work because those code parts responsible for freeing resources simply won't execute. If anybody else wanted to use those resources they may find them in inconsistent state and crash as well because they could not predict this particular situation.
说,你想要一个方法 M() 调用方法 N() 来做一些工作并安排一些资源,然后将它返回给 M(),M() 将使用它然后处理它.美好的.现在 N() 出了点问题,它抛出了一个你在 M() 中没有预料到的异常,所以异常冒泡到顶部,直到它可能被某些方法 C() 捕获,而 C() 将不知道发生了什么在 N() 中以及是否以及如何释放一些资源.
Say, you wanted a method M() call method N() to do some work and arrange for some resource then return it back to M() which will use it and then dispose it. Fine. Now something goes wrong in N() and it throws an exception you didn't expect in M() so the exception bubbles to the top until it maybe gets caught in some method C() which will have no idea what was happening deep down in N() and whether and how to free some resources.
通过抛出异常,您可以创建一种方法,使您的程序进入许多难以预测、理解和处理的新的不可预测的中间状态.这有点类似于使用 GOTO.设计一个可以随机将其执行从一个位置跳到另一个位置的程序是非常困难的.它也很难维护和调试.当程序变得越来越复杂时,您将失去对何时何地发生的事情的概述,从而减少修复它.
With throwing exceptions you create a way to bring your program into many new unpredictable intermediate states which are hard to prognose, understand and deal with. It's somewhat similar to using GOTO. It is very hard to design a program that can randomly jump its execution from one location to the other. It will also be hard to maintain and debug it. When the program grows in complexity, you just going to lose an overview of what when and where is happening less to fix it.
相关文章