代码契约组件是对NET的重要补充这次我们将提供更为详细的内容
如果要在NET 发布之前使用代码契约我们可以在Visual Studio项目中引用程序集MicrosoftContractsdll该程序集安装在%PROGRAMFILES%/Microsoft/Contracts/PublicAssemblies目录下NET 会在mscorlibdll中包含契约组件我们可以指定契约验证可在编译时(静态)或在运行时(动态)执行校验
契约包含几种类型前置条件(Preconditions)后置条件(Postconditions)对象不变量(Object Invariants)断言(Assertions)假定(Assumptions)量词(Quantifiers)接口契约(Interface Contracts)和抽象方法契约(Abstract Method Contracts)
前置条件使用ContractRequires()进行定义如果在编译时使用了符号(Symbol)CONTRACTS_FULL或CONTRACTS_PRECONDITIONS那么IL中就会包含其编译结果例如
Contract
Requires( x ! = null )
如下所示前置条件通常作为方法体中的参数验证如下所示
public Rational( int numerator
int denominator)
{ ContractRequires( denominator ! = );
this numerator = numerator;
this denominator = denominator;
}
如果不符合ContractRequires()指定的条件就会调用DebugAssert(false)然后调用EnvironmentFailFast()如果不管在编译时使用哪个符号您都希望程序集中包含前置条件那么可以使用ContractRequiresAlways()
当方法结束时后置条件表示其结果需要满足的契约它通过ContractEnsures()方法指定如下例所示
public int Denominator {
get {
ContractEnsures( ContractResult() != );
return this denominator;
}
}
虽然似乎在返回结果之前就指定了条件实际它还是会在返回结果之后调用者得到结果之前进行验证
对象不变量则为每个实例指定条件
ContractInvariantMethod]protected void ObjectInvariant () {
Contract Invariant ( this denominator ! = );
}
至于其他类型的契约断言表示为ContractAssert()假定则表示为ContractAssume()一个失败的Assert()会调用DebugAssert(false)假定与运行时断言相似不同之处在于静态检验的方式假定用于指定期望应该符合的条件而由于某些限制该条件无法得到编译器的验证
接口契约为接口指定条件它们使用在关联于接口的独立类上因为接口方法只能声明而不能拥有方法体对于抽象方法契约同样如此
以下为一个使用契约的类