将所有内容包装在 try/catch 块中是否构成防御性编程?
过去三年我一直在编程。 当我编程时,我通常会处理所有已知的异常并优雅地提醒用户。 我最近看到一些代码,其中几乎所有方法都包装在 try/catch 块中。 作者说这是防御性编程的一部分。 我想知道,这真的是防御性编程吗? 您是否建议将所有代码放在 try 块中?
I have been programming for the last 3 years. When I program, I use to handle all known exceptions and alert the user gracefully. I have seen some code recently which has almost all methods wrapped inside try/catch blocks. The author says it is part of defensive programming. I wonder, is this really defensive programming? Do you recommend putting all your code in try blocks?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我的基本规则是:除非你可以修复导致异常的问题,否则不要捕获它,让它冒泡到可以处理的水平。
根据我的经验,95% 的 catch 块要么只是忽略异常 (
catch {}
),要么只是记录错误并重新抛出异常。 后者似乎是正确的做法,但实际上,当在每个级别都这样做时,您最终只会在日志中塞满同一错误消息的五个副本。 通常这些应用程序在最顶层有一个“忽略捕获”(因为“我们在所有较低级别都有尝试/捕获”),导致应用程序非常慢,有很多错过的异常,并且错误日志太长任何人都愿意看它。My basic rule is : Unless you can fix the problem which caused the exception, do not catch it, let it bubble up to a level where it can be dealt with.
In my experience, 95% of all catch blocks either just ignore the exception (
catch {}
) or merely log the error and rethrow the exception. The latter might seem like the right thing to do, but in practice, when this is done at every level, you merely end up with your log cluttered with five copies of the same error message. Usually these apps have an "ignore catch" at the top most level (since "we have try/catch at all the lower levels"), resulting in a very slow app with lots of missed exceptions, and an error log that too long for anyone to be willing to look through it.大量使用 Try...Catch 并不是防御性编程,它只是将尸体钉在直立位置。
Try...Finally 可以广泛用于面对意外异常时的恢复。 仅当您期望出现异常并且现在如何处理它时,才应该使用 Try..Catch 代替。
有时我会看到 Try..Catch System.Exception,其中 catch 块仅记录异常并重新抛出。 这种方法至少存在 3 个问题:
Extensive use of Try...Catch isn't defensive programming, it's just nailing the corpse in an upright position.
Try...Finally can be used extensively for recovery in the face of unexpected exceptions. Only if you expect an exception and now how to deal with it should you use Try..Catch instead.
Sometimes I see Try..Catch System.Exception, where the catch block just logs the exception and re-throws. There are at least 3 problems with that approach:
不,这不是“防御性编程”。 你的同事试图通过用流行语来形容好习惯来合理化他的坏习惯。
他所做的事情应该被称为“掩盖事实”。 这就像统一
(void)
- 从方法调用中返回错误状态返回值。No, it's not "defensive programming." Your coworker is trying to rationalize his bad habit by employing a buzzword for a good habit.
What he's doing should be called "sweeping it under the rug." It's like uniformly
(void)
-ing the error-status return value from method calls.术语“防御性编程”代表以可以从错误情况中恢复或完全避免错误情况的方式编写代码。 例如:
如何处理 name == null? 你抛出异常还是接受它? 如果没有名称的对象没有意义,那么您应该抛出异常。 name == "" 怎么样?
但是......后来你写了一个编辑器。 在设置 UI 时,您会发现在某些情况下,用户可以决定删除该名称,或者在用户编辑名称时该名称可能会变为空。
另一个例子:
这里,防御策略通常是当 s == null 时返回 false(尽可能避免 NPE)。
或者:
防御性程序员可能会在 name == null 时返回“”,以避免调用代码中出现 NPE。
The term "defensive programming" stands for writing code in such a way that it can recover from error situations or that it avoid the error situation altogether. For example:
How do you handle name == null? Do you throw an exception or do you accept it? If it doesn't make sense to have an object without a name, then you should throw an exception. What about name == ""?
But ... later you write an editor. While you set up the UI, you find that there are situations where a user can decide to take the name away or the name can become empty while the user edits it.
Another example:
Here, the defensive strategy is often to return false when s == null (avoid NPEs when you can).
Or:
A defensive programmer might return "" if name == null to avoid NPEs in calling code.
如果您要处理随机异常,请仅在一个位置处理它们 - 应用程序的最顶部,目的是:
对于其他所有事情,您希望尽可能发生最直接的、特定于位置的崩溃,以便尽早捕获这些事情 - 否则异常处理就会成为隐藏草率设计和代码的一种方式。
在大多数情况下,异常是可预测的,可以提前测试异常处理程序将捕获的条件。
一般来说,If...Else 比 Try...Catch 好得多。
If you're going to handle random exceptions, handle them in only one place - the very top of the application, for the purposes of:
For everything else, you want the most immediate, location-specific crash possible, so that you catch these things as early as possible - otherwise exception handling becomes a way of hiding sloppy design and code.
In most cases where the exception is predictable, it's possible to test ahead of time, for the condition that the exception handler will catch.
In general, If...Else is much better than Try...Catch.
捕获随机异常是不好的。 然后怎样呢?
捕获实际上可以做一些有意义的事情的异常是很好的。 这些案例很容易识别和维护。
Catching random exceptions is bad. What then?
Catching exceptions for which you can actually do something meaningful is good. These cases are easy to identify and maintain.
我可以顺便说一句,每次我的一位同事写一个带有“抛出异常”的方法签名而不是列出该方法真正抛出的异常类型时,我想过去并把它们射在头上吗? 问题是,过了一段时间,您就会收到 14 级调用,所有这些调用都说“抛出异常”,因此重构以让它们声明它们真正抛出的内容是一项重大练习。
Can I just say as an aside here that every time one of my co-workers writes a method signature with "throws Exception" instead of listing the types of exceptions the method really throws, I want to go over and shoot them in the head? The problem is that after a while you've got 14 levels of calls all of which say "throws Exception", so refactoring to make them declare what they really throw is a major exercise.
存在“太多”处理这样的事情,捕获所有异常有点违背了要点。 特别是对于 C++,catch(...) 语句确实捕获所有异常,但您无法处理该异常的内容,因为您不知道异常的类型(并且它可以是任何类型)。
您应该捕获可以完全或部分处理的异常,并重新抛出部分异常。 您不应该捕获任何无法处理的异常,因为这只会混淆可能(或者更确切地说,将会)稍后困扰您的错误。
There is such a thing as "too much" processing, and catching all exceptions kindof defeats the point. Specifically for C++, the catch(...) statement does catch all exceptions but you can't process the contents of that exception because you don't know the type of the exception (and it could be anything).
You should catch the exceptions you can handle fully or partially, rethrowing the partial exceptions. You should not catch any exceptions that you can't handle, because that will just obfuscate errors that may (or rather, will) bite you later on.
我建议反对这种做法。 当您知道可以引发的异常类型时,将代码放入 try-catch 块中是一回事。 正如您所说,它允许您优雅地恢复和/或警告用户错误。 然而,将所有代码放在这样的块中,而您不知道可能会发生什么错误,这是使用异常来控制程序流程,这是一个很大的禁忌。
如果您正在编写结构良好的代码,您将了解可能发生的每个异常,并可以专门捕获这些异常。 如果您不知道如何引发特定异常,则不要捕获它,以防万一。 当它发生时,您可以找出异常、导致异常的原因,然后捕获它。
I would recommend against this practice. Putting code into try-catch blocks when you know the types of exceptions that can be thrown is one thing. It allows you, as you stated, to gracefully recover and/or alert the user as to the error. However, putting all your code inside such blocks where you don't know what the error is that may occur is using exceptions to control program flow, which is a big no-no.
If you are writing well-structured code, you will know about every exception that can occur, and can catch those specifically. If you don't know how a particular exception can be thrown, then don't catch it, just-in-case. When it happens, you can then figure out the exception, what caused it, and then catch it.
我想真正的答案是“这取决于”。 如果 try-catch 块捕获非常通用的异常,那么我会说这是防御性编程,就像从不开车出你的邻居是防御性驾驶一样。 try-catch (imo) 应针对特定异常进行定制。
再说一次,这只是我的观点,但我的防御性编程概念是,你需要更少/更小的 try-catch 块,而不是更多/更大的块。 您的代码应该尽一切努力确保异常情况从一开始就不会存在。
I guess the real answer is "It depends". If try-catch blocks are catching very generic exceptions then I would say it is defensive programming in the same way that never driving out of your neighborhood is defensive driving. A try-catch (imo) should be tailored to specific exceptions.
Again, this is just my opinion, but my concept of defensive programming is that you need fewer/smaller try-catch blocks not more/larger ones. Your code should be doing everything it can to make sure an exception condition can never exist in the first place.
在 C++ 中,编写大量 try/catch 块的原因之一是获取异常抛出位置的堆栈跟踪。 您要做的就是在各处编写一个 try/catch,并且(假设您没有在正确的位置处理异常)让 catch 记录一些跟踪信息,然后重新抛出异常。 这样,如果异常一直冒泡并导致程序终止,您将获得完整的堆栈跟踪,了解所有问题开始出现的位置(如果不这样做,则将出现未处理的 C++ 异常)有助于解开堆栈并消除您弄清楚它来自哪里的任何可能性)。
我可以想象,在任何具有更好的异常处理(即未捕获的异常告诉您它们来自哪里)的语言中,您只想捕获异常(如果您可以对它们做一些事情)。 否则,你只会让你的程序变得难以阅读。
In C++ the one reason to write lots of try/catch blocks is to get a stack trace of where the exception was thrown. What you do is write a try/catch everywhere, and (assuming you aren't at the right spot to deal with the exception) have the catch log some trace info then re-throw the exception. In this way, if an exception bubbles all the way up and causes the program to terminate, you'll have a full stack trace of where it all started to go wrong (if you don't do this, then an unhandled C++ exception will have helpfully unwound the stack and eradicated any possibility of you figuring out where it came from).
I would imagine that in any language with better exception handling (i.e. uncaught exceptions tell you where they came from) you'd want to only catch exceptions if you could do something about them. Otherwise, you're just making your program hard to read.
我发现“try”“catch”块非常有用,特别是如果使用任何实时功能(例如访问数据库)。
太多? 情人之眼。
我发现将日志复制到 Word,然后使用“查找”进行搜索(如果日志阅读器没有将“查找”或“搜索”作为其附带工具的一部分)是一种简单但极好的方法。详细日志。
从这个词的普通意义上来说,它确实看起来是“防御性”的。
根据经验,我发现无论你的经理、团队领导或同事做什么,都要遵循。 如果您只是为自己编程,请使用它们直到代码“稳定”或处于调试版本中,然后在完成后删除它们。
I've found "try" "catch" blocks to be very useful, especially if anything realtime (such as accessing a database) is used.
Too many? Eye of the beholder.
I've found that copying a log to Word, and searching with "find" -- if the log reader does not have "find" or "search" as part of its included tools -- is a simple but excellent way to slog through verbose logs.
It certainly seems "defensive" in the ordinary sense of the word.
I've found, through experience, to follow whatever your manager, team-leader, or co-worker does. If you're just programming for yourself, use them until the code is "stable" or in debug builds, and then remove them when done.