语言无关 – assert()vs enforce():选择哪个?

我很难选择是否应该“强制执行”条件或“断言”D中的条件.(但这是语言中立的.)

从理论上讲,我知道您使用断言来查找错误,并强制执行其他条件以检查非典型条件.例如.你可能会说你的方法的参数断言(count> = 0),因为这表明调用者有一个错误,你会说强制执行(isNetworkConnected),因为这不是一个错误,它只是你的东西我认为在你无法控制的合法情况下很可能不是真的.

此外,断言可以作为优化从代码中删除,没有副作用,但强制执行无法删除,因为它们必须始终执行其条件代码.因此,如果我实现一个充满惰性的容器,在第一次访问其任何方法时填充自己,我会强制执行(!empty())而不是assert(!empty()),因为检查为empty()必须始终发生,因为它懒惰地执行内部代码.

所以我想我知道他们应该是这个意思.但理论比实践更容易,而我实际上很难应用这些概念.

考虑以下:

我正在制作一个范围(类似于迭代器),它迭代两个其他范围,并添加结果. (对于函数式程序员:我知道我可以使用map!(“ab”)代替,但我现在忽略了它,因为它没有说明问题.)所以我的代码看起来像这样伪代码:

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();
    }
}

这应该是断言还是执法? (调用者的错误是范围不会同时消失吗?它可能无法控制范围来自哪里 – 它可能来自用户 – 但是再一次,它仍然看起来像一个小虫,不是吗?)

或者这是另一个伪代码示例:

uint getFileSize(string path)
{
    HANDLE hFile = CreateFile(path, ...);
    assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement?
    return GetFileSize(hFile); //and close the handle, obviously
}
...

这应该是断言还是强制执行?路径可能来自用户 – 因此它可能不是错误 – 但这个方法仍然是路径应该有效的先决条件.我是断言还是强制执行?

谢谢!

最佳答案 我不确定它是完全与语言无关的.我使用的语言没有强制执行(),如果我遇到了一个语言,那么我会想要以他们想要的方式使用assert和enforce,这可能是该语言的惯用语.

例如,C或C中的断言在程序失败时停止程序,它不会抛出异常,因此它的用法可能与您所说的不同.你不要在C中使用assert,除非你认为调用者已经犯了如此严重的错误而不能依赖它来清理(例如传递否定数),或者其他地方的其他代码已经做了如此严重的错误,应该认为程序处于未定义状态(例如,您的数据结构似乎已损坏).但是C确实区分了运行时错误和逻辑错误,这可能大致相当,但我认为主要是关于可避免和不可避免的错误.

在添加的情况下,如果作者的意图是提供不匹配列表的程序存在错误并需要修复,则使用逻辑错误,如果只是可能发生的事情之一,则运行时异常.例如,如果你的函数是处理任意生成器,那么它不一定能够报告它们的长度而不是破坏性地评估整个序列,你更可能认为它是一个不可避免的错误条件.

将其称为逻辑错误意味着调用者有责任在调用add之前检查长度,如果他们无法通过纯理由来确保它.所以他们不会在没有明确检查长度的情况下从用户传入列表,并且说实话,他们自己很幸运,他们甚至得到了异常而不是未定义的行为.

将其称为运行时错误表示传递不同长度的列表是“合理的”(如果异常),异常表明它发生在这种情况下.因此,我认为执法而不是断言.

在文件大小的情况下:对于文件的存在,您应该尽可能将其视为潜在的可恢复故障(强制执行),而不是错误(断言).原因很简单,调用者无法确定文件是否存在 – 总是有更多权限的人可以出现并删除它,或者在检查存在和调用之间卸载整个fielsystem.文件大小.因此,当调用代码不存在时,它不一定是调用代码中的逻辑缺陷(尽管最终用户可能已经在脚中射击).因为这个事实很可能会有呼叫者将其视为发生的事情之一,这是一个不可避免的错误情况.创建文件句柄也可能因内存不足而失败,这在大多数系统上是另一个不可避免的错误,但如果启用了过度提交,则不一定是可恢复的错误.

另一个需要考虑的例子是C [s]向量的operator []与at(). at()抛出out_of_range,一个逻辑错误,不是因为调用者可能想要恢复是不可思议的,或者因为你必须是某种麻木来使用at()来犯错误访问数组,但是因为如果调用者希望它是完全可以避免的错误 – 如果你没有其他方法可以知道你的索引是否正常,你可以在访问之前检查size().因此,operator []根本不保证任何检查,并且在效率名称中,超出范围的访问具有未定义的行为.

点赞