为什么要保守地使用异常?
我经常看到/听到人们说异常应该很少使用,但从不解释原因.虽然这可能是真的,但基本原理通常是一个油嘴滑舌:它被称为异常是有原因的",对我来说,这似乎是一种不应该被受人尊敬的程序员接受的解释/工程师.
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:如果可以让您的代码更简单、更容易,则绝对可以在一些不太例外的情况下使用异常.这个选项是开放的,但我想说它在实践中很少见.
@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 语句,它可以确保即使从 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(),它会使用它然后处理它.美好的.现在 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.
相关文章