Language agnostic 断言()与强制执行():选择哪一个?
我很难选择是在D中“强制”一个条件还是“断言”一个条件(尽管这是语言中立的) 理论上,我知道您使用断言来查找bug,并强制执行其他条件以检查非典型条件。例如,对于方法的参数,您可能会说Language agnostic 断言()与强制执行():选择哪一个?,language-agnostic,assert,assertions,enforcement,Language Agnostic,Assert,Assertions,Enforcement,我很难选择是在D中“强制”一个条件还是“断言”一个条件(尽管这是语言中立的) 理论上,我知道您使用断言来查找bug,并强制执行其他条件以检查非典型条件。例如,对于方法的参数,您可能会说assert(count>=0),因为这表明调用者存在错误,您会说exforce(isNetworkConnected),因为这不是错误,这只是一些你认为在你无法控制的合法情况下很可能不真实的事情 此外,断言可以作为优化从代码中删除,没有副作用,但强制不能删除,因为它们必须始终执行其条件代码。因此,如果我要实现一个
assert(count>=0)
,因为这表明调用者存在错误,您会说exforce(isNetworkConnected)
,因为这不是错误,这只是一些你认为在你无法控制的合法情况下很可能不真实的事情
此外,断言可以作为优化从代码中删除,没有副作用,但强制不能删除,因为它们必须始终执行其条件代码。因此,如果我要实现一个惰性填充容器,它在第一次访问它的任何方法时填充自己,我会说强制(!empty())
,而不是断言(!empty())
,因为empty()
的检查必须始终发生,因为它在内部惰性地执行代码
所以我想我知道他们的意思。但是理论比实践容易,我很难真正应用这些概念
考虑以下几点:
我正在创建一个范围(类似于迭代器),该范围迭代其他两个范围,并添加结果。(对于函数式程序员:我知道我可以使用map!(“a+b”)
,但是我现在忽略了这一点,因为它没有说明问题。)所以我有伪代码中类似的代码:
void add(Range range1, Range range2)
{
Range result;
while (!range1.empty)
{
assert(!range2.empty); //Should this be an assertion or enforcement?
result += range1.front + range2.front;
range1.popFront();
range2.popFront();
}
}
这是主张还是强制执行?(范围不同时为空是调用方的错吗?它可能无法控制范围的来源--它可能来自用户--但话说回来,它看起来仍然像一个bug,不是吗?)
下面是另一个伪代码示例:
uint getFileSize(string path)
{
HANDLE hFile = CreateFile(path, ...);
assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement?
return GetFileSize(hFile); //and close the handle, obviously
}
...
这是主张还是强制执行?该路径可能来自用户——因此可能不是bug——但该方法的前提条件仍然是该路径应有效。我是主张还是强制执行
谢谢 我相信你自己已经部分回答了你的问题。断言必然会破坏流程。如果你的断言是错误的,你将不会同意继续做任何事情。如果你强制执行某件事,你就是在根据情况做出允许某件事发生的决定。如果发现不符合条件,可以强制拒绝特定节的条目。我不确定它是否完全与语言无关。我使用的任何语言都没有
exforce()
,如果我遇到了这样的语言,那么我会希望按照它们预期的方式使用assert
和exforce
,这可能是该语言的惯用用法
例如,C或C++中的代码> AsStule当程序失败时停止程序,它不会抛出异常,因此它的用法可能与您所讨论的不一样。在C++中,除非你认为调用方已经犯了一个严重的错误,否则不能依赖于清理(例如,通过否定计数),否则其他地方的其他代码就犯了一个严重的错误,以致程序被认为处于未定义状态。(例如,您的数据结构看起来很坏)。C++确实区分运行时错误和逻辑错误,但大致上可以对应,但我认为主要是可避免的与不可避免的错误。
在add
的情况下,如果作者的意图是提供不匹配列表的程序存在错误并需要修复,则使用逻辑错误;如果只是可能发生的其中一种情况,则使用运行时异常。例如,如果您的函数要处理任意生成器,则不一定有报告的方法它们的长度短于对整个序列的破坏性评价,你更可能认为这是不可避免的错误条件。
将其称为逻辑错误意味着调用方有责任在调用add
之前检查长度,如果他们不能通过纯粹的理由来确保长度。因此,如果没有先明确检查长度,他们不会传递用户的列表,老实说,他们应该认为自己很幸运,他们甚至得到了一个exc概念而不是未定义的行为
将其称为运行时错误表示传入不同长度的列表是“合理的”(如果不正常的话),例外情况表明它发生在这种情况下。因此,我认为这是强制执行而不是断言
对于filesize
:对于文件的存在,如果可能,您应该将其视为潜在的可恢复故障(强制执行),而不是错误(断言)原因很简单,调用者无法确定某个文件是否存在——在检查文件是否存在和调用filesize
之间,总是会有拥有更多权限的人出现并删除该文件,或者卸载整个fielsystem。因此,当调用该文件时,调用代码中不一定存在逻辑缺陷不存在(尽管最终用户可能已经射中了自己的脚)。由于这一事实,可能会有调用方将其视为发生的事件之一,这是不可避免的错误情况。创建文件句柄也可能因内存不足而失败,这是大多数系统上另一个不可避免的错误,但如果启用了过度提交,则不一定是可恢复的错误
另一个例子是C++中的代码>操作程序[]/COD> vs>代码>()<代码> >代码> >(代码)>抛出<代码> OutoOfLange,逻辑错误,不是因为调用方可能想要恢复,否则必须是某种NUBBCALD,犯了从数组中访问数组的错误。