概述
代码契约之于C#语言,就相当于诗歌之于自然语言。
代码契约能从根本上改变代码的外观和所传达的信息量。
——《深入理解C#》(第二版)
Code Contract for .NET
使用之前必须先安装Code Contract for .NET,可以在
https://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-
1ce455f66970下载,安装之后visual studio的项目属性页中多了一页“Code
Contract”。
代码契约类型
前置条件
前置条件(Precondition)是对方法调用者提出要求,而不是表示普通条件下方法本身
的行为。我们总是坚信自己的代码没有问题,但却不能信任外部代码,因此前置条件是
已有程序中最常见的契约代码。前置契约使用Requires方法实现。
1
2 3 4 5 |
public static double Sqrt(double x) { Contract.Requires(x >= 0); return Math.Sqrt(x); } |
如果你在一个公共public方法中,对一个私有private变量使用前置条件契约,编译器会
直接报错,因为这样做对调用者是不公平的。
后置条件
前置条件是对方法输入或对象的原始状态的约束,而后置条件(Postcondition)是对方
法输出的约束:返回值、out或ref参数的值,以及任何被改变的状态。后置条件使用
Ensures方法实现。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class
MyTest { int m_nState = 0 ; //检查返回值。 public int Func1( int x) { Contract.Ensures(Contract.Result< int >() > 0 ); m_nState–; return x * 5 ; } //检查out参数,ref参数方法一样。 public void Func2( int x, out int y) { Contract.Ensures(Contract.ValueAtReturn< int >( out y) > 0 ); m_nState++; y = x * 3 ; } //检查本地状态 public int Func3( int x) { Contract.Ensures(Contract.ValueAtReturn< int >( out m_nState) > 0 ); return x + 1 ; } } |
后置条件的实现需要Contract.Result<T>()和Contract.ValueAtReturn<T>两个方法配
合实现。
后置条件最神奇的地方是契约代码写在return语句之前,可以省去定义一个临时变量存
放要返回的值,再对这个临时变量进行检查(手工断言)。这是代码契约与手工断言之
间最重要的区别。
固定条件/对象不变量
尽管叫invariant,但它并不表示一个保持不变的值,而是表示一个总是满足的固定条件
。例如一个字符串变量,在它可以在对象的生命周期内被不断修改,但永远满足不为空
的条件。固定条件可以在任何方法结束时进行自动检测,而不毕为防止出错,在每个方
法结束时对该变量进行检测。
固定条件的使用必须顶一个检测方法:必须是私有方法Private,必须用
ContractInvariantMethod特性修饰。
1
2 3 4 5 |
[ContractInvariantMethod] private void CheckState() { Contract.Invariant(m_nState > -10 && m_nState < 10); } |
断言和假设
Assert和Assume,如果不使用静态检查器,二者没有区别,它们和Debug.Assert方法类
似,在执行时检查某个条件是否为真。静态检查器处理这二个方法时略有不同,他会检
验断言是否为真,却不会处理假设。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public
int
RollDice(Random rng)
{ Contract.Ensures(Contract.Result< int >() >= 2 ); Contract.Ensures(Contract.Result< int >() <= 12 );Contract.Assert(rng != null ); int firstRoll = rng.Next( 1 , 7 ); Contract.Assume(firstRoll >= 1 ); Contract.Assume(firstRoll <= 6 ); int seconfRoll = rng.Next( 1 , 7 ); Contract.Assume(seconfRoll >= 1 ); Contract.Assume(seconfRoll <= 6 ); return firstRoll + seconfRoll; } |
最后
以上就是鳗鱼心锁为你收集整理的代码契约的全部内容,希望文章能够帮你解决代码契约所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复