I outlined my view on the state of the matter here: How do you validate an object's internal state? . Generally, assert your claims and throw for violation by others. For disabling asserts in release builds, you can do:
Disable asserts for expensive checks (like checking whether a range is ordered)
Keep trivial checks enabled (like checking for a null pointer or a boolean value)
Of course, in release builds, failed assertions and uncaught exceptions should be handled another way than in debug builds (where it could just call std::abort). Write a log of the error somewhere (possibly into a file), tell the customer that an internal error occurred. The customer will be able to send you the log-file.
There was a huge thread regarding the enabling/disabling of assertions in release builds on comp.lang.c++.moderated, which if you have a few weeks you can see how varied the opinions on this are. :)
Contrary to coppro, I believe that if you are not sure that an assertion can be disabled in a release build, then it should not have been an assert. Assertions are to protect against program invariants being broken. In such a case, as far as the client of your code is concerned there will be one of two possible outcomes:
Die with some kind of OS type failure, resulting in a call to abort. (Without assert)
Die via a direct call to abort. (With assert)
There is no difference to the user, however, it's possible that the assertions add an unnecessary performance cost in the code that is present in the vast majority of runs where the code doesn't fail.
The answer to the question actually depends much more on who the clients of the API will be. If you are writing a library providing an API, then you need some form of mechanism to notify your customers that they have used the API incorrectly. Unless you supply two versions of the library (one with asserts, one without) then assert is very unlikely the appropriate choice.
Personally, however, I'm not sure that I would go with exceptions for this case either. Exceptions are better suited to where a suitable form of recovery can take place. For example, it may be that you're trying to allocate memory. When you catch a 'std::bad_alloc' exception it might be possible to free up memory and try again.
you're asking about the difference between design-time and run-time errors.
asserts are 'hey programmer, this is broken' notifications, they're there to remind you of bugs you wouldn't have noticed when they happened.
exceptions are 'hey user, somethings gone wrong' notifications (obviously you can code to catch them so the user never gets told) but these are designed to occur at run time when Joe user is using the app.
So, if you think you can get all your bugs out, use exceptions only. If you think you can't..... use exceptions. You can still use debug asserts to make the number of exceptions less of course.
Don't forget that many of the preconditions will be user-supplied data, so you will need a good way of informing the user his data was no good. To do that, you'll often need to return error data down the call stack to the bits he is interacting with. Asserts will not be useful then - doubly so if your app is n-tier.
Lastly, I'd use neither - error codes are far superior for errors you think will occur regularly. :)
I prefer the second one. While your tests may have run fine, Murphy says that something unexpected will go wrong. So, instead of getting an exception at the actual erroneous method call, you end up tracing out a NullPointerException (or equivalent) 10 stack frames deeper.
前面的答案是正确的:对公共 API 函数使用异常。 您唯一可能希望改变此规则的情况是当检查的计算成本很高时。 在这种情况下,您可以将其放入断言中。
如果您认为可能违反该前提条件,请将其保留为例外,或重构该前提条件。
The previous answers are correct: use exceptions for public API functions. The only time you might wish to bend this rule is when the check is computationally expensive. In that case, you can put it in an assert.
If you think violation of that precondition is likely, keep it as an exception, or refactor the precondition away.
char *doSomething(char *ptr)
{
RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails.
// Goes away when debug off.
if( ptr != NULL )
{
...
}
return ptr;
}
You should use both. Asserts are for your convenience as a developer. Exceptions catch things you missed or didn't expect during runtime.
I've grown fond of glib's error reporting functions instead of plain old asserts. They behave like assert statements but instead of halting the program, they just return a value and let the program continue. It works surprisingly well, and as a bonus you get to see what happens to the rest of your program when a function doesn't return "what it's supposed to". If it crashes, you know that your error checking is lax somewhere else down the road.
In my last project, I used these style of functions to implement precondition checking, and if one of them failed, I would print a stack trace to the log file but keep on running. Saved me tons of debugging time when other people would encounter a problem when running my debug build.
Asserts are for catching something a developer has done wrong (not just yourself - another developer on your team also). If it's reasonable that a user mistake could create this condition, then it should be an exception.
Likewise think about the consequences. An assert typically shuts down the app. If there is any realistic expectation that the condition could be recovered from, you should probably use an exception.
On the other hand, if the problem can only be due to a programmer error then use an assert, because you want to know about it as soon as possible. An exception might be caught and handled, and you would never find out about it. And yes, you should disable asserts in the release code because there you want the app to recover if there is the slightest chance it might. Even if the state of your program is profoundly broken the user just might be able to save their work.
It is not exactly true that "assert fails only in debug mode."
In Object Oriented Software Construction, 2nd Edition by Bertrand Meyer, the author leaves a door open for checking preconditions in release mode. In that case, what happens when an assertion fails is that... an assertion violation exception is raised! In this case, there is no recovery from the situation: something useful could be done though, and it is to automatically generate an error report and, in some cases, to restart the application.
The motivation behind this is that preconditions are typically cheaper to test than invariants and postconditions, and that in some cases correctness and "safety" in the release build are more important than speed. i.e. For many applications speed is not an issue, but robustness (the ability of the program to behave in a safe way when its behaviour is not correct, i.e. when a contract is broken) is.
Should you always leave precondition checks enabled? It depends. It's up to you. There is no universal answer. If you're making software for a bank, it might be better to interrupt execution with an alarming message than to transfer $1,000,000 instead of $1,000. But what if you're programming a game? Maybe you need all the speed you can get, and if someone gets 1000 points instead of 10 because of a bug that the preconditions didn't catch (because they're not enabled), tough luck.
In both cases you should ideally have catched that bug during testing, and you should do a significant part of your testing with assertions enabled. What is being discussed here is what is the best policy for those rare cases in which preconditions fail in production code in a scenario which was not detected earlier due to incomplete testing.
To summarize, you can have assertions and still get the exceptions automatically, if you leave them enabled - at least in Eiffel. I think to do the same in C++ you need to type it yourself.
I tried synthesising several of the other answers here with my own views.
Use assertions for cases where you want to disable it in production, erring toward leaving them in. The only real reason to disable in production, but not in development, is to speed up the program. In most cases, this speed up won't be significant, but sometimes code is time critical or the test is computationally expensive. If code is mission critical, then exceptions may be best despite the slow down.
If there is any real chance of recovery, use an exception as assertions aren't designed to be recovered from. For example, code is rarely designed to recover from programming errors, but it is designed to recover from factors such as network failures or locked files. Errors should not be handled as exceptions simply for being outside the control of the programmer. Rather, the predictability of these errors, compared to coding mistakes, makes them more amiable to recovery.
Re argument that it is easier to debug assertions: The stack trace from a properly named exception is as easy to read as an assertion. Good code should only catch specific types of exceptions, so exceptions should not go unnoticed due to being caught. However, I think Java sometimes forces you to catch all exceptions.
I some cases, asserts are disabled when building for release. You may not have control over this (otherwise, you could build with asserts on), so it might be a good idea to do it like this.
The problem with "correcting" the input values is that the caller will not get what they expect, and this can lead to problems or even crashes in wholly different parts of the program, making debugging a nightmare.
I usually throw an exception in the if-statement to take over the role of the assert in case they are disabled
assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
The rule of thumb, to me, is that use assert expressions to find internal errors and exceptions for external errors. You can benefit much from the following discussion by Greg from here.
Assert expressions are used to find programming errors: either errors in the program's logic itself or in errors in its corresponding implementation. An assert condition verifies that the program remains in a defined state. A "defined state" is basically one that agrees with the program's assumptions. Note that a "defined state" for a program need not be an "ideal state" or even "a usual state", or even a "useful state" but more on that important point later.
To understand how assertions fit into a program, consider a routine in a C++ program that is about to dereference a pointer. Now should the routine test whether the pointer is NULL before the dereferencing, or should it assert that the pointer is not NULL and then go ahead and dereference it regardless?
I imagine that most developers would want to do both, add the assert, but also check the pointer for a NULL value, in order not to crash should the asserted condition fail. On the surface, performing both the test and the check may seem the wisest decision
Unlike its asserted conditions, a program's error handling (exceptions) refers not to errors in the program, but to inputs the program obtains from its environment. These are often "errors" on someone's part, such as a user attempting to login to an account without typing in a password. And even though the error may prevent a successful completion of program's task, there is no program failure. The program fails to login the user without a password due to an external error - an error on the user's part. If the circumstances were different, and the user typed in the correct password and the program failed to recognize it; then although the outcome would still be the same, the failure would now belong to the program.
The purpose of error handling (exceptions) is two fold. The first is to communicate to the user (or some other client) that an error in program's input has been detected and what it means. The second aim is to restore the application after the error is detected, to a well-defined state. Note that the program itself is not in error in this situation. Granted, the program may be in a non-ideal state, or even a state in which can do nothing useful, but there is no programming errorl. On the contrary, since the error recovery state is one anticipated by the program's design, it iss one that the program can handle.
The principle I follow is this: If a situation can be realistically avoided by coding then use an assertion. Otherwise use an exception.
Assertions are for ensuring that the Contract is being adhered to. The contract must be fair, so that client must be in a position to ensure it complies. For example, you can state in a contract that a URL must be valid because the rules about what is and isn't a valid URL are known and consistent.
Exceptions are for situations that are outside the control of both the client and the server. An exception means that something has gone wrong, and there's nothing that could have been done to avoid it. For example, network connectivity is outside the applications control so there is nothing that can be done to avoid a network error.
I'd like to add that the Assertion / Exception distinction isn't really the best way to think about it. What you really want to be thinking about is the contract and how it can be enforced. In my URL example above that best thing to do is have a class that encapsulates a URL and is either Null or a valid URL. It is the conversion of a string into a URL that enforces the contract, and an exception is thrown if it is invalid. A method with a URL parameter is much clearer that a method with a String parameter and an assertion that specifies a URL.
Disabling assert in release builds is like saying "I will never have any issues whatsoever in a release build", which is often not the case. So assert shouldn't be disabled in a release build. But you don't want the release build crashing whenever errors occur either, do you?
So use exceptions and use them well. Use a good, solid exception hierarchy and ensure that you catch and you can put a hook on exception throwing in your debugger to catch it, and in release mode you can compensate for the error rather than a straight-up crash. It's the safer way to go.
经验法则是,当您尝试捕获自己的错误时应该使用断言,而当尝试捕获其他人的错误时应该使用异常。 换句话说,您应该使用异常来检查公共 API 函数的先决条件,以及每当您获取系统外部的任何数据时。 您应该对系统内部的函数或数据使用断言。
The rule of thumb is that you should use assertions when you are trying to catch your own errors, and exceptions when trying to catch other people's errors. In other words, you should use exceptions to check the preconditions for the public API functions, and whenever you get any data that are external to your system. You should use asserts for the functions or data that are internal to your system.
发布评论
评论(14)
我在这里概述了我对此事状况的看法:如何您验证对象的内部状态吗?。 一般来说,坚持你的主张并指责他人违反。 要在发布版本中禁用断言,您可以执行以下操作:
当然,在发布版本中,失败的断言未捕获的异常应该以不同于调试版本的其他方式处理(它可以只调用 std::abort)。 将错误日志写入某处(可能写入文件),告诉客户发生了内部错误。 客户将能够向您发送日志文件。
I outlined my view on the state of the matter here: How do you validate an object's internal state? . Generally, assert your claims and throw for violation by others. For disabling asserts in release builds, you can do:
Of course, in release builds, failed assertions and uncaught exceptions should be handled another way than in debug builds (where it could just call std::abort). Write a log of the error somewhere (possibly into a file), tell the customer that an internal error occurred. The customer will be able to send you the log-file.
有一个巨大的讨论帖 关于在 comp.lang.c++.moderated 上构建的版本中启用/禁用断言,如果您有几周的时间,您可以看到对此的意见有多么不同。 :)
与 coppro 相反,我相信如果您不确定是否可以在发布版本中禁用断言,那么它不应该是断言。 断言是为了防止程序不变量被破坏。 在这种情况下,就代码的客户端而言,将有两种可能的结果之一:
对用户来说没有区别,但是,断言可能会在代码中增加不必要的性能成本,而这种成本存在于代码不会失败的绝大多数运行中。
这个问题的答案实际上更多地取决于 API 的客户是谁。 如果您正在编写一个提供 API 的库,那么您需要某种形式的机制来通知您的客户他们错误地使用了 API。 除非您提供两个版本的库(一种带断言,一种不带),否则断言不太可能是合适的选择。
然而,就我个人而言,我也不确定我是否会在这种情况下例外。 例外更适合可以进行适当形式的恢复的情况。 例如,您可能正在尝试分配内存。 当您捕获“std::bad_alloc”异常时,可能会释放内存并重试。
There was a huge thread regarding the enabling/disabling of assertions in release builds on comp.lang.c++.moderated, which if you have a few weeks you can see how varied the opinions on this are. :)
Contrary to coppro, I believe that if you are not sure that an assertion can be disabled in a release build, then it should not have been an assert. Assertions are to protect against program invariants being broken. In such a case, as far as the client of your code is concerned there will be one of two possible outcomes:
There is no difference to the user, however, it's possible that the assertions add an unnecessary performance cost in the code that is present in the vast majority of runs where the code doesn't fail.
The answer to the question actually depends much more on who the clients of the API will be. If you are writing a library providing an API, then you need some form of mechanism to notify your customers that they have used the API incorrectly. Unless you supply two versions of the library (one with asserts, one without) then assert is very unlikely the appropriate choice.
Personally, however, I'm not sure that I would go with exceptions for this case either. Exceptions are better suited to where a suitable form of recovery can take place. For example, it may be that you're trying to allocate memory. When you catch a 'std::bad_alloc' exception it might be possible to free up memory and try again.
您问的是设计时错误和运行时错误之间的区别。
断言是“嘿程序员,这坏了”通知,它们的作用是提醒您发生错误时您不会注意到的错误。
例外情况是“嘿用户,出了点问题”通知(显然您可以编写代码来捕获它们,这样用户就永远不会被告知),但这些通知被设计为在 Joe 用户使用应用程序时在运行时发生。
因此,如果您认为可以消除所有错误,请仅使用异常。 如果你认为你不能......使用例外。 当然,您仍然可以使用调试断言来减少异常数量。
不要忘记,许多先决条件将是用户提供的数据,因此您需要一种好方法来通知用户他的数据不好。 为此,您通常需要将错误数据沿着调用堆栈返回到他正在交互的位。 那么断言就没有用了——如果你的应用程序是n层的,那么断言就没有用处了。
最后,我不会使用任何一个 - 错误代码对于您认为经常发生的错误要优越得多。 :)
you're asking about the difference between design-time and run-time errors.
asserts are 'hey programmer, this is broken' notifications, they're there to remind you of bugs you wouldn't have noticed when they happened.
exceptions are 'hey user, somethings gone wrong' notifications (obviously you can code to catch them so the user never gets told) but these are designed to occur at run time when Joe user is using the app.
So, if you think you can get all your bugs out, use exceptions only. If you think you can't..... use exceptions. You can still use debug asserts to make the number of exceptions less of course.
Don't forget that many of the preconditions will be user-supplied data, so you will need a good way of informing the user his data was no good. To do that, you'll often need to return error data down the call stack to the bits he is interacting with. Asserts will not be useful then - doubly so if your app is n-tier.
Lastly, I'd use neither - error codes are far superior for errors you think will occur regularly. :)
我更喜欢第二个。 虽然您的测试可能运行良好,但 Murphy 表示会出现意想不到的问题。 因此,您最终不会在实际错误的方法调用中获得异常,而是会在 10 个堆栈帧深处追踪出 NullPointerException(或等效项)。
I prefer the second one. While your tests may have run fine, Murphy says that something unexpected will go wrong. So, instead of getting an exception at the actual erroneous method call, you end up tracing out a NullPointerException (or equivalent) 10 stack frames deeper.
前面的答案是正确的:对公共 API 函数使用异常。 您唯一可能希望改变此规则的情况是当检查的计算成本很高时。 在这种情况下,您可以将其放入断言中。
如果您认为可能违反该前提条件,请将其保留为例外,或重构该前提条件。
The previous answers are correct: use exceptions for public API functions. The only time you might wish to bend this rule is when the check is computationally expensive. In that case, you can put it in an assert.
If you think violation of that precondition is likely, keep it as an exception, or refactor the precondition away.
你应该同时使用两者。 断言是为了方便开发人员。 异常会捕获运行时错过或未预料到的事情。
我越来越喜欢 glib 的错误报告函数,而不是普通的旧断言。 它们的行为类似于断言语句,但它们不会停止程序,而是返回一个值并让程序继续运行。 它的工作效果出奇的好,而且作为奖励,当函数没有返回“它应该返回的内容”时,您可以看到程序的其余部分会发生什么。 如果它崩溃了,你就知道你的错误检查在其他地方是松懈的。
在我的上一个项目中,我使用这些风格的函数来实现前置条件检查,如果其中一个失败,我会将堆栈跟踪打印到日志文件,但继续运行。 当其他人在运行我的调试版本时遇到问题时,为我节省了大量的调试时间。
如果我需要运行时检查参数,我会这样做:
You should use both. Asserts are for your convenience as a developer. Exceptions catch things you missed or didn't expect during runtime.
I've grown fond of glib's error reporting functions instead of plain old asserts. They behave like assert statements but instead of halting the program, they just return a value and let the program continue. It works surprisingly well, and as a bonus you get to see what happens to the rest of your program when a function doesn't return "what it's supposed to". If it crashes, you know that your error checking is lax somewhere else down the road.
In my last project, I used these style of functions to implement precondition checking, and if one of them failed, I would print a stack trace to the log file but keep on running. Saved me tons of debugging time when other people would encounter a problem when running my debug build.
If I needed runtime checking of arguments, I'd do this:
断言用于捕获开发人员做错的事情(不仅仅是您自己,还有您团队中的其他开发人员)。 如果用户错误可能造成这种情况是合理的,那么它应该是一个例外。
同样考虑后果。 断言通常会关闭应用程序。 如果有任何现实的期望可以从该情况中恢复,您可能应该使用异常。
另一方面,如果问题仅是由于程序员错误造成的,则使用断言,因为您想尽快了解它。 异常可能会被捕获并处理,但您永远不会发现它。 是的,您应该在发布代码中禁用断言,因为您希望应用程序在有任何可能的情况下恢复。 即使程序的状态严重损坏,用户也可能能够保存他们的工作。
Asserts are for catching something a developer has done wrong (not just yourself - another developer on your team also). If it's reasonable that a user mistake could create this condition, then it should be an exception.
Likewise think about the consequences. An assert typically shuts down the app. If there is any realistic expectation that the condition could be recovered from, you should probably use an exception.
On the other hand, if the problem can only be due to a programmer error then use an assert, because you want to know about it as soon as possible. An exception might be caught and handled, and you would never find out about it. And yes, you should disable asserts in the release code because there you want the app to recover if there is the slightest chance it might. Even if the state of your program is profoundly broken the user just might be able to save their work.
“断言仅在调试模式下失败”并不完全正确。
在 Bertrand Meyer 的面向对象软件构建,第二版中,作者为在发布模式下检查先决条件敞开了大门。 在这种情况下,当断言失败时会发生......引发断言违规异常! 在这种情况下,无法从这种情况中恢复:尽管可以做一些有用的事情,它是自动生成错误报告,并且在某些情况下重新启动应用程序。
其背后的动机是,先决条件通常比不变量和后置条件的测试成本更便宜,并且在某些情况下,发布版本中的正确性和“安全性”比速度更重要。 即,对于许多应用程序来说,速度不是问题,但鲁棒性(程序在其行为不正确时(即合同被破坏时)以安全方式运行的能力)才是问题。
您是否应该始终启用前提条件检查? 这取决于。 由你决定。 没有普遍的答案。 如果您正在为银行开发软件,那么最好用警报消息来中断执行,而不是转账 1,000,000 美元而不是 1,000 美元。 但是如果您正在编写游戏程序怎么办? 也许您需要尽可能快的速度,如果有人因为先决条件未捕获的错误(因为未启用)而获得 1000 分而不是 10 分,那么运气不好。
在这两种情况下,理想情况下您应该在测试期间捕获该错误,并且您应该在启用断言的情况下完成测试的重要部分。 这里讨论的是对于那些罕见情况的最佳策略,在这种情况下,在由于测试不完整而未提前检测到的情况下,前提条件在生产代码中失败。
总而言之,如果您启用断言,仍然可以自动获取异常(至少在 Eiffel 中)。 我认为在 C++ 中做同样的事情你需要自己输入。
另请参阅:断言何时应保留在生产代码中?
It is not exactly true that "assert fails only in debug mode."
In Object Oriented Software Construction, 2nd Edition by Bertrand Meyer, the author leaves a door open for checking preconditions in release mode. In that case, what happens when an assertion fails is that... an assertion violation exception is raised! In this case, there is no recovery from the situation: something useful could be done though, and it is to automatically generate an error report and, in some cases, to restart the application.
The motivation behind this is that preconditions are typically cheaper to test than invariants and postconditions, and that in some cases correctness and "safety" in the release build are more important than speed. i.e. For many applications speed is not an issue, but robustness (the ability of the program to behave in a safe way when its behaviour is not correct, i.e. when a contract is broken) is.
Should you always leave precondition checks enabled? It depends. It's up to you. There is no universal answer. If you're making software for a bank, it might be better to interrupt execution with an alarming message than to transfer $1,000,000 instead of $1,000. But what if you're programming a game? Maybe you need all the speed you can get, and if someone gets 1000 points instead of 10 because of a bug that the preconditions didn't catch (because they're not enabled), tough luck.
In both cases you should ideally have catched that bug during testing, and you should do a significant part of your testing with assertions enabled. What is being discussed here is what is the best policy for those rare cases in which preconditions fail in production code in a scenario which was not detected earlier due to incomplete testing.
To summarize, you can have assertions and still get the exceptions automatically, if you leave them enabled - at least in Eiffel. I think to do the same in C++ you need to type it yourself.
See also: When should assertions stay in production code?
我尝试将其他几个答案与我自己的观点综合起来。
在您想要在生产中禁用它的情况下使用断言,而错误地将它们保留在其中。在生产中禁用而不是在开发中禁用的唯一真正原因是加快程序速度。 在大多数情况下,这种加速并不显着,但有时代码对时间要求很高,或者测试的计算成本很高。 如果代码是关键任务,那么尽管速度较慢,但异常可能是最好的。
如果确实有恢复的机会,请使用异常,因为断言并不是为了恢复而设计的。 例如,代码很少被设计为从编程错误中恢复,但它被设计为从网络故障或锁定文件等因素中恢复。 错误不应仅仅因为超出了程序员的控制范围而被视为异常。 相反,与编码错误相比,这些错误的可预测性使它们更容易恢复。
重新争论调试断言更容易:来自正确命名的异常的堆栈跟踪与断言一样易于阅读。 好的代码应该只捕获特定类型的异常,因此异常不应该因为被捕获而被忽视。 然而,我认为 Java 有时会强迫你捕获所有异常。
I tried synthesising several of the other answers here with my own views.
Use assertions for cases where you want to disable it in production, erring toward leaving them in. The only real reason to disable in production, but not in development, is to speed up the program. In most cases, this speed up won't be significant, but sometimes code is time critical or the test is computationally expensive. If code is mission critical, then exceptions may be best despite the slow down.
If there is any real chance of recovery, use an exception as assertions aren't designed to be recovered from. For example, code is rarely designed to recover from programming errors, but it is designed to recover from factors such as network failures or locked files. Errors should not be handled as exceptions simply for being outside the control of the programmer. Rather, the predictability of these errors, compared to coding mistakes, makes them more amiable to recovery.
Re argument that it is easier to debug assertions: The stack trace from a properly named exception is as easy to read as an assertion. Good code should only catch specific types of exceptions, so exceptions should not go unnoticed due to being caught. However, I think Java sometimes forces you to catch all exceptions.
另请参阅此问题:
See also this question:
对我来说,经验法则是使用断言表达式来查找内部错误和外部错误的异常。 您可以从 Greg 在此处。
PS:您可能想查看类似的问题:异常与断言。
The rule of thumb, to me, is that use assert expressions to find internal errors and exceptions for external errors. You can benefit much from the following discussion by Greg from here.
PS: you may want to check out the similar question: Exception Vs Assertion.
我遵循的原则是:如果可以通过编码切实避免某种情况,则使用断言。 否则使用异常。
断言是为了确保遵守合同。 合同必须公平,以便客户必须能够确保其遵守。 例如,您可以在合同中声明 URL 必须有效,因为关于什么是有效 URL 和什么不是有效 URL 的规则是已知的且一致的。
例外情况是客户端和服务器都无法控制的情况。 异常意味着出现了问题,并且无法采取任何措施来避免它。 例如,网络连接不受应用程序控制,因此无法采取任何措施来避免网络错误。
我想补充一点,断言/异常的区别并不是真正考虑它的最佳方式。 您真正想要考虑的是合同以及如何执行它。 在上面的 URL 示例中,最好的办法是拥有一个封装 URL 的类,并且该类可以是 Null 或有效的 URL。 它是将字符串转换为 URL 来强制执行契约,如果无效则抛出异常。 具有 URL 参数的方法比具有 String 参数和指定 URL 的断言的方法更清晰。
The principle I follow is this: If a situation can be realistically avoided by coding then use an assertion. Otherwise use an exception.
Assertions are for ensuring that the Contract is being adhered to. The contract must be fair, so that client must be in a position to ensure it complies. For example, you can state in a contract that a URL must be valid because the rules about what is and isn't a valid URL are known and consistent.
Exceptions are for situations that are outside the control of both the client and the server. An exception means that something has gone wrong, and there's nothing that could have been done to avoid it. For example, network connectivity is outside the applications control so there is nothing that can be done to avoid a network error.
I'd like to add that the Assertion / Exception distinction isn't really the best way to think about it. What you really want to be thinking about is the contract and how it can be enforced. In my URL example above that best thing to do is have a class that encapsulates a URL and is either Null or a valid URL. It is the conversion of a string into a URL that enforces the contract, and an exception is thrown if it is invalid. A method with a URL parameter is much clearer that a method with a String parameter and an assertion that specifies a URL.
在发布版本中禁用断言就像是在说“我在发布版本中永远不会遇到任何问题”,但事实通常并非如此。 因此,不应在发布版本中禁用断言。 但您也不希望发布版本在发生错误时崩溃,对吗?
所以要使用异常并且用好它们。 使用良好、可靠的异常层次结构并确保捕获,并且可以在调试器中抛出异常来捕获它,并且在发布模式下您可以补偿错误而不是直接崩溃。 这是更安全的方法。
Disabling assert in release builds is like saying "I will never have any issues whatsoever in a release build", which is often not the case. So assert shouldn't be disabled in a release build. But you don't want the release build crashing whenever errors occur either, do you?
So use exceptions and use them well. Use a good, solid exception hierarchy and ensure that you catch and you can put a hook on exception throwing in your debugger to catch it, and in release mode you can compensate for the error rather than a straight-up crash. It's the safer way to go.
经验法则是,当您尝试捕获自己的错误时应该使用断言,而当尝试捕获其他人的错误时应该使用异常。 换句话说,您应该使用异常来检查公共 API 函数的先决条件,以及每当您获取系统外部的任何数据时。 您应该对系统内部的函数或数据使用断言。
The rule of thumb is that you should use assertions when you are trying to catch your own errors, and exceptions when trying to catch other people's errors. In other words, you should use exceptions to check the preconditions for the public API functions, and whenever you get any data that are external to your system. You should use asserts for the functions or data that are internal to your system.