我很难选择是否应该“强制执行”条件或“断言”D中的条件。(但这是语言中立的。)
从理论上讲,我知道您使用断言来查找错误,并强制执行其他条件以检查非典型条件。例如。您可能会说assert(count >= 0)
为您的方法的参数,因为这表明调用者存在错误,并且您会说enforce(isNetworkConnected)
,因为这不是错误,它只是您的问题所在假设在你无法控制的合理情况下很可能不是真的。
此外,断言可以作为优化从代码中删除,没有副作用,但是强制执行无法删除,因为它们必须始终执行其条件代码。因此,如果我正在实现一个充满惰性的容器,在第一次访问其任何方法时填充它,我说enforce(!empty())
而不是assert(!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();
}
}
这应该是断言还是执法? (调用者的错误是范围不会同时消失吗?它可能无法控制范围来自哪里 - 它可能来自用户 - 但是再一次,它仍然看起来像一个小虫,不是吗?)
或者这是另一个伪代码示例:
uint getFileSize(string path)
{
HANDLE hFile = CreateFile(path, ...);
assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement?
return GetFileSize(hFile); //and close the handle, obviously
}
...
这应该是断言还是强制执行?路径可能来自用户 - 因此它可能不是错误 - 但这个方法仍然是路径应该有效的先决条件。我是断言还是强制执行?
谢谢!
答案 0 :(得分:1)
我不确定它是完全与语言无关的。我使用的语言没有 enforce()
,如果我遇到了一种语言,那么我会想要按照预期的方式使用assert
和enforce
,这可能是那种语言的惯用语。
例如,C或C ++中的assert
在程序失败时停止程序,它不会抛出异常,因此它的用法可能与您所说的不同。你不要在C ++中使用assert
,除非你认为调用者已经犯了如此严重的错误而不能依赖它来清理(例如传递负数),或者其他一些错误其他地方的代码已经犯了如此严重的错误,以至于该程序应被视为处于未定义状态(例如,您的数据结构似乎已损坏)。 C ++确实区分了运行时错误和逻辑错误,但这可能大致相当,但我认为主要是关于可避免和不可避免的错误。
在add
的情况下,如果作者的意图是提供不匹配列表的程序存在错误并需要修复,或者运行时异常(如果它只是其中之一),则会使用逻辑错误发生。例如,如果你的函数是处理任意生成器,那么它不一定能够报告它们的长度,而不是破坏性地评估整个序列,你更可能认为这是一个不可避免的错误条件。
将其称为逻辑错误意味着调用者有责任在调用add
之前检查长度,如果他们无法通过行使纯粹理由来确保它。所以他们不会在没有明确检查长度的情况下从用户传递列表,并且诚实地认为他们自己很幸运他们甚至得到异常而不是未定义的行为。
将其称为运行时错误表示传递不同长度的列表是“合理的”(如果异常),异常表示它在此时发生。因此,我认为是执法而不是断言。
对于filesize
:对于文件的存在,您应该尽可能将其视为潜在的可恢复故障(强制执行),而不是错误(断言)。原因很简单,调用者无法确定文件是否存在 - 总是有更多权限的人可以出现并删除它,或者在检查存在和调用之间卸载整个fielsystem。 filesize
。因此,当调用代码不存在时,它不一定是调用代码中的逻辑缺陷(尽管最终用户可能已经在脚中射击)。因为这个事实很可能会有呼叫者将其视为发生的事情之一,这是一个不可避免的错误情况。创建文件句柄也可能因内存不足而失败,这在大多数系统上是另一个不可避免的错误,但如果例如启用了过度提交,则不一定是可恢复的错误。
要考虑的另一个例子是operator[]
与at()
的C ++向量。 at()
抛出out_of_range
,这是一个逻辑错误,并不是因为调用者可能想要恢复是不可思议的,或者因为你必须要某种麻木来犯错误访问数组超出范围at()
,但因为如果调用者希望错误是完全可以避免的 - 如果你没有其他方法知道你的索引是否正常,你可以在访问之前检查size()
。因此operator[]
根本不保证任何检查,并且以效率的名义,超出范围的访问具有未定义的行为。
答案 1 :(得分:1)
assert
应该被认为是“运行时检查的注释”,表明程序员在那时做出的假设。 assert
是函数实现的一部分。失败的assert
在作出错误假设时应始终被视为错误,因此在断言的代码位置也应视为错误。要修复该错误,请使用适当的方法来避免这种情况。
避免错误的函数输入的正确方法是协定,因此示例函数应该具有一个输入协定,该协定检查range2至少与range1一样长。这样,实现内部的断言仍然可以保留。特别是在更长,更复杂的实现中,这样的断言可能会提高可理解性。
enforce
是一种引发运行时异常的惰性方法。这对于快速处理肮脏的代码非常有用,因为最好在那里进行检查,而不是静默地忽略发生不良情况的可能性。对于生产代码,应使用引发更有意义的异常的适当机制代替它。
答案 2 :(得分:0)
我相信你自己已经部分回答了你的问题。断言必将打破这种流动。如果您的断言是错误的,您将不同意继续任何事情。如果你执行某些事情,你就会根据情况决定是否允许某些事情发生。如果发现不满足条件,则可以强制拒绝特定部分的条目。