后置条件是一段代码(例如方法或函数)在函数退出时给出关于状态的保证,例如返回值的正确性,或者更广泛的状态(例如状态)的正确性类实例甚至整个程序。
我会将 a 解释Post-Condition Exception
为引发异常的操作(直接使用throw
或使用 guard Assert
),而不是仅仅轻轻地返回失败的返回码或默认值,调用者可能不会检查这些值。
硬故障在检查前置条件、后置条件和不变量时至关重要,因为调用者需要检查(并且可能会错过)软返回(例如false或0或-1之类的魔法值)并掩盖代码在设计外状态下运行的真正问题。
以下示例希望说明使用异常的后置条件。在我的简单设计中Square(x)
,只要输入的契约是有效的(即输入数字的平方不会溢出),函数应该保证结果是一个正数。如果后置条件检查失败,则意味着我的设计/实现中存在缺陷,这可能会产生可怕的后果(例如,没有考虑到的场景,或依赖项失败,例如Math
库本身)。
有例外的例子:
public static double Square(double number)
{
// Pre condition
if (Math.Abs(number) > Math.Sqrt(double.MaxValue))
throw new InvalidArgumentException("Number too big - will overflow");
var result = number * number;
// Post condition
if (result < 0)
throw new Exception("Square(x) should always be positive!");
return result;
}
根据评论,代码合同还允许通过Contract.Ensures
. 这样做的好处是,在方法的顶部记录了前置条件和后置条件,并且还避免了对额外局部变量的需要,因为可以直接“检查”结果:
public static double Square(double number)
{
Contract.Requires(Math.Abs(number) < Math.Sqrt(double.MaxValue),
"Oops number will result in overflow");
Contract.Ensures(Contract.Result<double>() >= 0,
"Square should always be positive!");
return number * number;
}
代码合约相对于基于异常的断言的另一个优势是静态检查——合约可以在编译时进行验证。