一个后置条件是关于代码状态的保证,例如方法或函数退出时返回值的正确性,或更广泛的状态,例如类实例或整个程序的状态的正确性。
我认为“后置条件异常”指的是抛出异常的操作(直接使用throw或使用守卫Assert),而不仅仅是软性返回失败的返回码或默认值,因为调用者可能不会检查这些值。
在前置条件、后置条件和不变量的检查中,硬性故障至关重要,因为软性返回需要由调用者检查(并可能被忽略),并掩盖了“代码正在以非设计状态运行”的真正问题。
以下示例希望用异常说明后置条件。在我简单的Square(x)设计中,只要输入数字的合同有效(即输入数字平方不会溢出),该函数应保证结果是正数。如果后置条件检查失败,这意味着我的设计/实现存在缺陷,可能会产生严重后果(例如未考虑的情况或依赖项失败,例如Math库本身)。
以下是一个带有异常的示例:
public static double Square(double number)
{
if (Math.Abs(number) > Math.Sqrt(double.MaxValue))
throw new InvalidArgumentException("Number too big - will overflow");
var result = number * number;
if (result < 0)
throw new Exception("Square(x) should always be positive!");
return result;
}
根据评论,
Code Contracts还允许通过
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;
}
与基于异常的断言相比,代码契约具有的另一个优势是静态检查- 契约可以在编译后立即验证。