我正在做一些作业,其中有一个问题:
如何处理前置条件异常?
如何使用后置条件异常?
对于第一个问题,我假设前提条件是为了使函数运行而必须满足的东西。例如,参数不能为空,可以抛出异常:
if (myArg == null)
throw new ArgumentNullException("myArg");
//do work here
然而,我不明白后置条件异常的用途是什么,以及为什么我们要在这种情况下抛出异常(例如,如果不满足条件而不是返回false)。
谁能提供一个例子吗?
后验条件是一段代码(如方法或函数)在退出函数时给出的状态保证,如返回值的正确性,或更广泛状态的正确性,如类实例甚至整个程序的状态。
我将Post-Condition Exception
解释为抛出异常的动作(直接使用throw
,或使用保护Assert
),而不仅仅是软返回失败的返回代码,或默认值,这可能不会被调用者检查。
硬故障在检查前置条件、后置条件和不变量方面是至关重要的,因为软返回(例如像false或0或-1这样的魔术值)需要被调用者检查(并且可能被忽略),并且掩盖了代码在非设计状态下运行的真正问题。
下面的示例有望说明使用异常的post条件。在我对Square(x)
的简化设计中,假设输入上的契约是有效的(即输入数的平方不会溢出),该函数应该保证结果是正数。如果post条件检查失败,这意味着我的设计/实现中存在缺陷,可能会产生可怕的后果(例如,没有想到的场景,或者依赖项的失败,例如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
指定post条件。这样做的好处是,前置和后置条件都记录在方法的顶部,并且还避免了对额外的局部变量的需要,因为结果可以直接"检查":
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;
}
代码契约相对于基于异常的断言的另一个优点是静态检查——契约可以在编译后进行验证。
另一个答案是正确的,评论对回答你的问题都很有用。我觉得有一点可以补充的,所以这是我的贡献。
我也从来没有听说过它被称为"后条件异常",但至于你的问题,"什么时候使用它们",它们在单元测试中使用得很多(参见https://stackoverflow.com/tags/unit-testing/info或其他资源,如维基百科的操作方法)。在单元测试中,测试程序员调用她想要测试的函数,并且她也知道她期望从函数中得到什么值(也就是说,从其他知识来源知道答案是正确的)。后置条件测试是:"从函数或方法返回的期望值和实际值是否一致?"
如果它们不一致,则Assert方法抛出异常。测试框架软件捕获异常,然后将给定的测试报告为失败,并继续运行其他测试,而不会在失败的测试上停止。下面是一个简单的例子。
[TestMethod]
public void Integers_7Add1_equals8()
{
int expected = 8;
int actual = MyClass.AddOne(7);
Assert.AreEqual(expected, actual);
}
异常在Assert中抛出。在调用被测试的方法之后调用AreEqual方法,因此它是一个后置条件异常。