Go语言的创造者写道:
Go doesn't provide assertions. They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting. Proper error handling means that servers continue operation after non-fatal errors instead of crashing. Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace. Precise errors are particularly important when the programmer seeing the errors is not familiar with the code.
你对此有什么看法?
是的,断言是邪恶的。
它们通常用于应该使用正确错误处理的地方。从一开始就要习惯编写正确的产品质量错误处理程序!
通常它们会妨碍编写单元测试(除非您编写了与测试工具交互的自定义断言)。这通常是因为它们被用于应该使用正确错误处理的地方。
大多数情况下,它们是从发布版本中编译出来的,这意味着当你运行实际发布的代码时,它们的“测试”是不可用的;考虑到在多线程情况下,最糟糕的问题通常只出现在发布代码中,这可能很糟糕。
有时,他们是一个拐杖,否则破碎的设计;也就是说,代码的设计允许用户以一种不应该被调用的方式调用它,而断言“阻止”了这一点。修改设计!
早在2005年,我就在我的博客http://www.lenholgate.com/blog/2005/09/assert-is-evil.html上写过更多关于这方面的内容
如果您所谈论的断言意味着程序抛出然后存在,那么断言可能非常糟糕。这并不是说它们总是错误的使用,它们是一种很容易被误用的结构。他们也有很多更好的选择。这样的事情很容易被称为邪恶。
例如,第三方模块(或任何模块)几乎不应该退出调用程序。这并没有给调用程序的程序员任何控制程序此时应该承担的风险。在许多情况下,数据是如此重要,即使保存损坏的数据也比丢失数据要好。断言可能会迫使您丢失数据。
断言的一些替代方法:
使用调试器,
控制台/数据库/其他日志
异常
其他类型的错误处理
参考:
http://ftp.gnu.org/old-gnu/Manuals/nana-1.14/html_node/nana_3.html
http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
Go不提供断言,并且有很好的理由:http://golang.org/doc/faq#assertions
http://c2.com/cgi/wiki?DoNotUseAssertions
甚至主张assert的人也认为它们应该只用于开发而不是生产:
http://codebetter.com/gregyoung/2007/12/12/asserts-are-not-evil/
http://www.codeproject.com/Articles/6404/Assert-is-your-friend
http://parabellumgames.wordpress.com/using-asserts-for-debugging/
This person says that asserts should be used when the module has potentially corrupted data that persists after an exception is thrown: http://www.advogato.org/article/949.html . This is certainly a reasonable point, however, an external module should never prescribe how important corrupted data is to the calling program (by exiting "for" them). The proper way to handle this is by throwing an exception that makes it clear that the program may now be in an inconsistent state. And since good programs mostly consist of modules (with a little glue code in the main executable), asserts are almost always the wrong thing to do.
我最近开始在我的代码中添加一些断言,这是我一直在做的:
我在心里把代码分为边界代码和内部代码。边界代码是处理用户输入、读取文件和从网络获取数据的代码。在这段代码中,我在一个循环中请求输入,该循环仅在输入有效时退出(在交互式用户输入的情况下),或者在不可恢复的文件/网络损坏数据的情况下抛出异常。
内部代码就是一切。例如,在我的类中设置变量的函数可以定义为
void Class::f (int value) {
assert (value < end);
member = value;
}
从网络获取输入的函数可以这样读:
void Class::g (InMessage & msg) {
int const value = msg.read_int();
if (value >= end)
throw InvalidServerData();
f (value);
}
This gives me two layers of checks. Anything where the data is determined at run-time always gets an exception or immediate error handling. However, that extra check in Class::f with the assert statement means that if some internal code ever calls Class::f, I still have a sanity check. My internal code might not pass a valid argument (because I may have calculated value from some complex series of functions), so I like having the assertion in the setting function to document that regardless of who is calling the function, value must not be greater than or equal to end.
这似乎符合我在一些地方读到的内容,即在一个功能良好的程序中,断言应该是不可能违反的,而例外应该是针对仍然可能发生的异常和错误情况。因为理论上我要验证所有输入,所以我的断言不应该被触发。如果是,我的程序就错了。
我更倾向于避免在调试和发布中做不同事情的代码。
但是,在一个条件下中断调试器并获得所有文件/行信息,以及确切的表达式和确切的值是有用的。
拥有一个“只在调试中评估条件”的断言可能是一种性能优化,因此,它只在0.0001%的程序中有用——人们知道他们在做什么。在所有其他情况下,这是有害的,因为表达式实际上可能改变程序的状态:
assert(2 == shroedingerscat . getnummears ());
会使程序在调试和发布时做不同的事情。
我们开发了一组assert宏,可以在调试版和发布版中抛出异常。例如,THROW_UNLESS_EQ(a, 20);what()消息同时包含文件、行和a的实际值,等等,将抛出异常。只有宏才有这个功能。调试器可以配置为在特定异常类型的'throw'时中断。