我已经阅读了很多关于如何以及何时使用断言的文章(以及在StackOverflow上发布的其他几个类似问题),我对它们理解得很好.但是,我仍然不明白应该驱使我使用什么样的动机Debug.Assert而不是抛出一个普通的例外.我的意思是,在.NET中,对失败的断言的默认响应是"停止世界"并向用户显示一个消息框.虽然可以修改这种行为,但我觉得这样做非常烦人和多余,而我可以改为抛出一个合适的异常.这样,我可以在抛出异常之前轻松地将错误写入应用程序的日志,而且,我的应用程序不一定会冻结.
那么,为什么我应该使用Debug.Assert而不是普通的例外呢?将断言放在不应该出现的地方可能会导致各种"不需要的行为",所以在我看来,我真的没有通过使用断言而不是抛出异常来获得任何东西.你同意我的意见,还是我在这里遗漏了什么?
注意:我完全理解"在理论上"(调试与发布,使用模式等)之间的区别是什么,但正如我所看到的,我最好抛出异常而不是执行断言.因为如果在生产版本上发现了一个bug,我仍然希望"断言"失败(毕竟,"开销"非常小),所以我最好不要抛出异常.
编辑:我看到它的方式,如果断言失败,这意味着应用程序进入某种已损坏的意外状态.那么我为什么要继续执行呢?应用程序是否在调试版或发行版上运行并不重要.两者都是如此
Eri*_*ert 169
虽然我同意你的推理是合理的 - 也就是说,如果一个断言意外地被违反,通过抛出来暂停执行是有道理的 - 我个人不会在断言的位置使用异常.原因如下:
正如其他人所说,主张应记录那些情况是不可能的,以这样的方式,如果涉嫌不可能的情况来传递,开发商通知.相反,例外情况为异常,不太可能或错误的情况提供了控制流机制,但并非不可能的情况.对我来说,关键的区别在于:
始终应该可以生成一个测试用例来执行给定的throw语句.如果无法生成这样的测试用例,那么程序中的代码路径将永远不会执行,并且应该作为死代码删除.
永远不可能产生一个导致断言发射的测试用例.如果断言触发,则代码错误或断言错误; 无论哪种方式,都需要在代码中进行更改.
这就是为什么我不会用异常替换断言.如果断言实际上无法触发,则将其替换为异常意味着您的程序中存在不可测试的代码路径.我不喜欢不可测试的代码路径.
Ned*_*der 15
断言用于检查程序员对世界的理解.只有程序员做错了,断言才会失败.例如,永远不要使用断言来检查用户输入.
断言测试"不可能发生"的情况.例外情况是"不应该发生而是发生"的情况.
断言很有用,因为在构建时(甚至运行时),您可以更改其行为.例如,通常在发布版本中,甚至不检查断言,因为它们会引入不必要的开销.这也是需要警惕的:您的测试甚至可能无法执行.
如果使用异常而不是断言,则会丢失一些值:
代码更冗长,因为测试和抛出异常至少有两行,而断言只有一行.
您的测试和抛出代码将始终运行,而断言可以编译掉.
您失去了与其他开发人员的一些沟通,因为断言与检查和抛出的产品代码具有不同的含义.如果您确实在测试编程断言,请使用断言.
更多信息:http://nedbatchelder.com/text/assert.html
Tom*_*and 11
编辑: 响应您在帖子中所做的编辑/注释:听起来使用异常是正确的事情,使用断言来表示您想要完成的事情类型.我认为你遇到的心理障碍是你正在考虑例外和断言来实现同样的目的,所以你试图弄清楚哪一个是"正确的"使用.虽然在如何使用断言和例外方面可能存在一些重叠,但不要混淆它们对于同一问题的不同解决方案 - 它们不是.断言和例外都有自己的目的,优点和缺点.
我打算用我自己的话说出一个答案,但这样做的概念比我的理念更好:
使用assert语句可以是在运行时捕获程序逻辑错误的有效方法,但它们很容易从生产代码中过滤掉.一旦开发完成,只需在编译期间定义预处理器符号NDEBUG [禁用所有断言],就可以消除这些编码错误冗余测试的运行时成本.但是,请务必记住,在生产版本中将省略置于断言本身的代码.
断言最好用于仅在以下所有条件成立时测试条件:
Run Code Online (Sandbox Code Playgroud)* the condition should never be false if the code is correct, * the condition is not so trivial so as to obviously be always true, and * the condition is in some sense internal to a body of software.断言几乎不应用于检测软件正常运行期间出现的情况. 例如,通常不应使用断言来检查用户输入中的错误.但是,使用断言来验证调用者是否已经检查了用户的输入可能是有意义的.
基本上,对生产应用程序中需要捕获/处理的事物使用异常,使用断言执行逻辑检查,这些检查对于开发很有用,但在生产中关闭.
我认为一个(人为的)实际例子可能有助于阐明差异:
(改编自MoreLinq的批量扩展)
// 'public facing' method
public int DoSomething(List<string> stuff, object doohickey, int limit) {
// validate user input and report problems externally with exceptions
if(stuff == null) throw new ArgumentNullException("stuff");
if(doohickey == null) throw new ArgumentNullException("doohickey");
if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");
return DoSomethingImpl(stuff, doohickey, limit);
}
// 'developer only' method
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {
// validate input that should only come from other programming methods
// which we have control over (e.g. we already validated user input in
// the calling method above), so anything using this method shouldn't
// need to report problems externally, and compilation mode can remove
// this "unnecessary" check from production
Debug.Assert(stuff != null);
Debug.Assert(doohickey != null);
Debug.Assert(limit > 0);
/* now do the actual work... */
}
Run Code Online (Sandbox Code Playgroud)
正如Eric Lippert等人所说,你只断言你期望是正确的东西,以防你(开发人员)在其他地方意外地使用它,所以你可以修复你的代码.当您无法控制或无法预测进入的内容时,您基本上会抛出异常,例如对于用户输入,以便无论什么给它带来坏数据都可以适当地响应(例如用户).