Language agnostic 断言()与强制执行():选择哪一个?

Language agnostic 断言()与强制执行():选择哪一个?,language-agnostic,assert,assertions,enforcement,Language Agnostic,Assert,Assertions,Enforcement,我很难选择是在D中“强制”一个条件还是“断言”一个条件(尽管这是语言中立的) 理论上,我知道您使用断言来查找bug,并强制执行其他条件以检查非典型条件。例如,对于方法的参数,您可能会说assert(count>=0),因为这表明调用者存在错误,您会说exforce(isNetworkConnected),因为这不是错误,这只是一些你认为在你无法控制的合法情况下很可能不真实的事情 此外,断言可以作为优化从代码中删除,没有副作用,但强制不能删除,因为它们必须始终执行其条件代码。因此,如果我要实现一个

我很难选择是在D中“强制”一个条件还是“断言”一个条件(尽管这是语言中立的)

理论上,我知道您使用断言来查找bug,并强制执行其他条件以检查非典型条件。例如,对于方法的参数,您可能会说
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,犯了从数组中访问数组的错误。