Code Contracts的命名空间:System.Diagnostics.Contracts

集合1.

安装Code Contracts for .NET插件
Contracts.devlab9ts.msi

安装了该插件后,可以在项目的属性页上发现多了一个Code Contracts的标签:

勾上"Perform Runtime Check"选项,只是可以看到右侧的下拉框有五个选项,这里分别介绍一下它们的区别:

  1. Full表示执行所有的代码协定语句。
  2. Pre and Post表示执行前置和后置条件检查,即Contract.Require和Contract.Ensures。
  3. Preconditions 表示只执行前置条件检查,即Contract.Require。
  4. ReleaseRequires 表示执行public类的public方法的前置条件检查。
  5. None表示不执行代码协定检查,即不进行代码协定注入。

之所以有这些选项,是因为代码协定的代码注入运行时候是有开销的,影响运行性能。另外,链接的时候注入代码也是影响程序的生成时间的。我们可以在需要检验功能的时候放开代码检查,需要性能的时候关闭检查,非常方便,而传统的手动抛异常的方式就做不到这一点。

值得一提的是,它是可以在Debug版本和Release版本中选择不同的开关的。我们在Debug版本中开启必要的代码检查,而在Release版本中关闭检查。需要注意的是,public类的public方法的入参检查(前置条件协定)是有必要的,即使在Release版本中也也应该存在。我的个人建议是在Debug版本中平时使用Pre and Post选项(注入代码也是影响程序的生成时间的,故只在有必要的时候才使用Full的方式),在Release版本中使用ReleaseRequires选项。

另外,在安装了Code Contracts for .NET插件后,它也顺带安装了一些代码片段,方便我们快速输入,非常给力。例如Contract.Require函数可以直接通过"cr"+Tab键输入,Contract.Ensures可以通过"ce"+Tab键输入。

集合2.

Project Setup

After you’ve downloaded and installed Code Contracts and created a library project, you need to enable CC for that library. Under the Project Properties, there is a new tab called Code Contracts. Here’s the way that I like to set it up:

  • Set “Assembly Mode” to “Standard Contract Requires”.
  • Debug - Check “Perform Runtime Contract Checking” and set to “Full”. Check “Call-site Requires Checking”.

  • Release - Set “Contract Reference Assembly” to “Build” and check “Emit contracts into XML doc file”.

In addition, if you have the Academic or Commercial Premium edition of Code Contracts, add a configuration called CodeAnalysis with all the settings from Debug and also check “Perform Static Contract Checking” and all the other checkboxes in that section except “Baseline”.

This will give you three separate builds, with separate behavior:

  • CodeAnalysis - This evaluates all the code contracts at build time, searching for any errors. This “build” doesn’t result in a usable dll, but should be run before checking in code, similar to a unit test.
  • Debug - This turns on all code contracts at runtime. This includes code contract checks for any libraries that your library uses (such as CLR libraries). It also turns on consistency checks such as Contract.Assert and ContractInvariantMethod.
  • Release - This turns off all code contracts in your dll at runtime. However, it includes a separate “.Contracts.dll” which contains the contracts for your library’s public API.

Projects consuming your library should reference your Release build. In their Debug configuration, they should check “Perform Runtime Contract Checking” and “Call-site Requires Checking”; this will ensure that the code contracts for your library’s public API are enforced at runtime (the “Call-site” option uses the “.Contracts.dll” assembly that you built). In their Release configuration, they should leave code contracts disabled, which allows all assemblies to run at full speed.

If the consuming project suspects a bug in your library (i.e., their Debug build doesn’t cause any Contracts violations but your library is still not behaving as expected), they can remove the reference to your Release build and add a reference to your Debug build. This is an easy way to enable all the code contract checks in your library, even the internal ones.

Preconditions (Contract.Requires)

Preconditions require some condition at the beginning of a method. Common examples are requiring a parameter to be non-null, or to require the object to be in a particular state.

public string GetObjectInfo(object obj)
{
Contract.Requires(obj != null);
return obj.ToString();
}

Postconditions (Contract.Ensures)

Postconditions guarantee some condition at the end of a method. It is often used with Contract.Result to guarantee that a particular method won’t return null.

private string name; // never null
public string Name
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
return this.name;
}
}

Preconditions and Postconditions on Interfaces

Both preconditions and postconditions are commonly placed on interface members. Code Contracts includes the ContractClassAttribute and ContractClassForAttribute to facilitate this:

[ContractClass(typeof(MyInterfaceContracts))]
public interface IMyInterface
{
string GetObjectInfo(object obj);
string Name { get; }
} [ContractClassFor(typeof(IMyInterface))]
internal abstract class MyInterfaceContracts : IMyInterface
{
public string GetObjectInfo(object obj)
{
Contract.Requires(obj != null);
return null; // fake implementation
} public string Name
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
return null; // fake implementation does not need to satisfy postcondition
}
}
}

With generic interfaces, the same idea holds:

[ContractClass(typeof(MyInterfaceContracts<,>))]
public interface IMyInterface<T, U>
{
string GetObjectInfo(object obj);
string Name { get; }
} [ContractClassFor(typeof(IMyInterface<,>))]
internal abstract class MyInterfaceContracts<T, U> : IMyInterface<T, U>
{
public string GetObjectInfo(object obj)
{
Contract.Requires(obj != null);
return null; // fake implementation
} public string Name
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
return null; // fake implementation does not need to satisfy postcondition
}
}
}

Invariants (ContractInvariantMethod, Contract.Invariant)

Object invariants are expressed using the ContractInvariantMethod. If they are enabled by the build, then they are checked at the beginning of each method (except constructors) and at the end of each method (except Dispose and the finalizer).

public class MyClass<T, U>: public IMyInterface<T, U>
{
private string name; public MyClass(string name)
{
Contract.Requires(name != null);
this.name = name;
} [ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(this.name != null);
} public string Name
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
return this.name;
}
} public string GetObjectInfo(object obj)
{
Contract.Requires(obj != null);
return obj.ToString();
}
}

Assertions and Assumptions (Contract.Assert, Contract.Assume)

There will always be some things that should be true but just have to be checked at runtime. For these, use Contract.Assert unless the static checker (i.e., the CodeAnalysis configuration) complains. You can then change them to be Contract.Assume so that the static checker can use them. There’s no difference between Contract.Assert and Contract.Assume at runtime.

Reminder: if you’re using the Release build setup recommended above, then all your Contract.Assertand Contract.Assume calls get removed from your release builds. So they can’t be used to throw vexing exceptions, e.g., rejecting invalid input.

In the example below, the static checker would complain because Type.MakeGenericType has preconditions that are difficult to prove. So we give it a little help by inserting some Contract.Assumecalls, and the static checker is then pacified.

public static IMyInterface<T, U> CreateUsingReflection()
{
var openGenericReturnType = typeof(MyClass<,>);
Contract.Assume(openGenericReturnType.IsGenericTypeDefinition);
Contract.Assume(openGenericReturnType.GetGenericArguments().Length == 2);
var constructedGenericReturnType = openGenericReturnType.MakeGenericType(typeof(T), typeof(U));
return (IMyInterface<T, U>)Activator.CreateInstance(constructedGenericReturnType);
}

For More Information

The Code Contracts library has a thorough user manual available. It’s a bit of a hard read, but they include a lot of information that I’ve skipped for this “intro” post, such as:

  • Specifying postconditions that hold even if the method throws an exception.
  • Techniques for gradually migrating Code Contracts into an existing library.
  • Details on how Code Contracts are inherited.
  • Contract abbreviations.
  • Applying contracts to sequences (e.g., ForAll and Exists quantifiers).
  • Advanced contract checking with Pure methods.
  • Tips for working with the static checker.

集合3

1. Assert

Assert(断言)是最基本的契约。.NET 4.0 使用 Contract.Assert() 方法来特指断言。它用来表示程序点必须保持的一个契约。

Contract.Assert(this.privateField > 0);
Contract.Assert(this.x == 3, "Why isn’t the value of x 3?");

断言有两个重载方法,首参数都是一个布尔表达式,第二个方法的第二个参数表示违反契约时的异常信息。

当断言运行时失败,.NET CLR 仅仅调用 Debug.Assert 方法。成功时则什么也不做。

2. Assume

.NET 4.0 使用 Contract.Assume() 方法表示 Assume(假设) 契约。

Contract.Assume(this.privateField > 0);
Contract.Assume(this.x == 3, "Static checker assumed this");

Assume 契约在运行时检测的行为与 Assert(断言) 契约完全一致。但对于静态验证来说,Assume 契约仅仅验证已添加的事实。由于诸多限制,静态验证并不能保证该契约。或许最好先使用 Assert 契约,然后在验证代码时按需修改。

当 Assume 契约运行时失败时, .NET CLR 会调用 Debug.Assert(false)。同样,成功时什么也不做。

3. Preconditions

.NET 4.0 使用 Contract.Requires() 方法表示 Preconditions(前置条件) 契约。它表示方法被调用时方法状态的契约,通常被用来做参数验证。所有 Preconditions 契约相关成员,至少方法本身可以访问。

Contract.Requires(x != null);

Preconditions 契约的运行时行为依赖于几个因素。如果只隐式定义了 CONTRACTS PRECONDITIONS 标记,而没有定义 CONTRACTS_FULL 标记,那么只会进行检测 Preconditions 契约,而不会检测任何 Postconditions 和 Invariants 契约。假如违反了 Preconditions 契约,那么 CLR 会调用 Debug.Assert(false) 和 Environment.FastFail 方法。

假如想保证 Preconditions 契约在任何编译中都发挥作用,可以使用下面这个方法:

Contract.RequiresAlways(x != null);

为了保持向后兼容性,当已存在的代码不允许被修改时,我们需要抛出指定的精确异常。但是在 Preconditions 契约中,有一些格式上的限定。如下代码所示:

if (x == null) throw new ArgumentException("The argument can not be null.");
Contract.EndContractBlock(); // 前面所有的 if 检测语句皆是 Preconditions 契约

这种 Preconditions 契约的格式严格受限:它必须严格按照上述代码示例格式。而且不能有 else 从句。此外,then 从句也只能有单个 throw 语句。最后必须使用 Contract.EndContractBlock() 方法来标记 Preconditions 契约结束。

看到这里,是不是觉得大多数参数验证都可以被 Preconditions 契约替代?没有错,事实的确如此。这样这些防御性代码完全可以在 Release 被去掉,从而不用做那些冗余的代码检测,从而提高程序性能。但在面向验证客户输入此类情境下,防御性代码仍有必要。再就是,Microsoft 为了保持兼容性,并没有用 Preconditions 契约代替异常。

4. Postconditions

Postconditions 契约表示方法终止时的状态。它跟 Preconditions 契约的运行时行为完全一致。但与 Preconditions 契约不同,Postconditions 契约相关的成员有着更少的可见性。客户程序或许不会理解或使用 Postconditions 契约表示的信息,但这并不影响客户程序正确使用 API 。

对于 Preconditions 契约来说,它则对客户程序有副作用:不能保证客户程序不违反 Preconditions 契约。

A. 标准 Postconditions 契约用法

.NET 4.0 使用 Contract.Ensures() 方法表示标准 Postconditions 契约用法。它表示方法正常终止时必须保持的契约。

Contract.Ensures(this.F > 0);
B. 特殊 Postconditions 契约用法

当从方法体内抛出一个特定异常时,通常情况下 .NET CLR 会从方法体内抛出异常的位置直接跳出,从而辗转堆栈进行异常处理。假如我们需要在异常抛出时还要进行 Postconditions 契约验证,我们可以如下使用:

Contract.EnsuresOnThrows<T>(this.F > 0);

其中小括号内的参数表示当异常从方法内抛出时必须保持的契约,而泛型参数表示异常发生时抛出的异常类型。举例来说,当我们把 T 用 Exception 表示时,无论什么类型的异常被抛出,都能保证 Postconditions 契约。哪怕这个异常是堆栈溢出或任何不能控制的异常。强烈推荐当异常是被调用 API 一部分时,使用 Contract.EnsuresOnThrows<T>() 方法。

C. Postconditions 契约内的特殊方法

以下要讲的这几个特殊方法仅限使用在 Postconditions 契约内。

方法返回值 在 Postconditions 契约内,可以通过 Contract.Result<T>() 方法表示,其中 T 表示方法返回类型。当编译器不能推导出 T 类型时,我们必须显式指出。比如,C# 编译器就不能推导出方法参数类型。

Contract.Ensures(0 < Contract.Result<int>());

假如方法返回 void ,则不必在 Postconditions 契约内使用 Contract.Result<T>() 。

前值(旧值)  在 Postconditions 契约内,通过 Contract.OldValue<T>(e) 表示旧有值,其中 T 是 e 的类型。当编译器能够推导 T 类型时,可以忽略。此外 e 和旧有表达式出现上下文有一些限制。旧有表达式只能出现在 Postconditions 契约内。旧有表达式不能包含另一个旧有表达式。一个很重要的原则就是旧有表达式只能引用方法已经存在的那些旧值。比如,只要方法 Preconditions 契约持有,它必定能被计算。下面是这个原则的一些示例:

  • 方法的旧有状态必定存在其值。比如 Preconditions 契约暗含 xs != null ,xs 当然可以被计算。但是,假如 Preconditions 契约为 xs != null || E(E 为任意表达式),那么 xs 就有可能不能被计算。
Contract.OldValue(xs.Length);  // 很可能错误
  • 方法返回值不能被旧有表达式引用。
Contract.OldValue(Contract.Result<int>() + x);  // 错误
  • out 参数也不能被旧有表达式引用。
  • 如果某些标记的方法依赖方法返回值,那么这些方法也不能被旧有表达式引用。
Contract.ForAll(0, Contract.Result<int>(), i => Contract.OldValue(xs[i]) > 3);  // 错误
  • 旧有表达式不能在 Contract.ForAll() 和 Contract.Exists() 方法内引用匿名委托参数,除非旧有表达式被用作索引器或方法调用参数。
Contract.ForAll(0, xs.Length, i => Contract.OldValue(xs[i]) > 3);  // OK
Contract.ForAll(0, xs.Length, i => Contract.OldValue(i) > 3);  // 错误
  • 如果旧有表达式依赖于匿名委托的参数,那么旧有表达式不能在匿名委托的方法体内。除非匿名委托是 Contract.ForAll() 和 Contract.Exists() 方法的参数。
Foo( ... (T t) => Contract.OldValue(... t ...) ... ); // 错误
D. out 参数

因为契约出现在方法体前面,所以大多数编译器不允许在 Postconditions 契约内引用 out 参数。为了绕开这个问题,.NET 契约库提供了 Contract.ValueAtReturn<T>(out T t) 方法。

public void OutParam(out int x)
{
Contract.Ensures(Contract.ValueAtReturn(out x) == 3);
x = 3;
}

跟 OldValue 一样,当编译器能推导出类型时,泛型参数可以被忽略。该方法只能出现在 Postconditions 契约。方法参数必须是 out 参数,且不允许使用表达式。

需要注意的是,.NET 目前的工具不能检测确保 out 参数是否正确初始化,而不管它是否在 Postconditions 契约内。因此, x = 3 语句假如被赋予其他值时,编译器并不能发现错误。但是,当编译 Release 版本时,编译器将发现该问题。

5. Object Invariants

对象不变量表示无论对象是否对客户程序可见,类的每一个实例都应该保持的契约。它表示对象处于一个“良好”状态。

在 .NET 4.0 中,对象的所有不变量都应当放入一个受保护的返回 void 的实例方法中。同时用[ContractInvariantMethod]特性标记该方法。此外,该方法体内在调用一系列 Contract.Invariant() 方法后不能再有其他代码。通常我们会把该方法命名为 ObjectInvariant 。

[ContractInvariantMethod]
protected void ObjectInvariant()
{
Contract.Invariant(this.y >= 0);
Contract.Invariant(this.x > this.y);
}

同样,Object Invariants 契约的运行时行为和 Preconditions 契约、Postconditions 契约行为一致。CLR 运行时会在每个公共方法末端检测 Object Invariants 契约,但不会检测对象终结器或任何实现 System.IDisposable 接口的方法。

Contract 静态类中的其他特殊方法

.NET 4.0 契约库中的 Contract 静态类还提供了几个特殊的方法。它们分别是:

A. ForAll

Contract.ForAll() 方法有两个重载。第一个重载有两个参数:一个集合和一个谓词。谓词表示返回布尔值的一元方法,且该谓词应用于集合中的每一个元素。任何一个元素让谓词返回 false ,ForAll 停止迭代并返回 false 。否则, ForAll 返回 true 。下面是一个数组内所有元素都不能为 null 的契约示例:

public T[] Foo<T>(T[] array)
{
Contract.Requires(Contract.ForAll(array, (T x) => x != null));
}
B. Exists

它和 ForAll 方法差不多。

注:摘自

http://www.cnblogs.com/lucifer1982/archive/2009/03/21/1418642.html

http://blog.stephencleary.com/2011/01/simple-and-easy-code-contracts.html

c#Code Contracts代码协定的更多相关文章

  1. Code Snippets 代码片段

    Code Snippets 代码片段       1.Title : 代码片段的标题 2.Summary : 代码片段的描述文字 3.Platform : 可以使用代码片段的平台,有IOS/OS X/ ...

  2. Effective Java提升Code Coverage代码涵盖率 - 就是爱Java

    虽然我们已经有了测试程序,但是如何得知是否已完整测试了主程序?,透过Code Coverage代码涵盖率,我们可以快速地得知,目前系统中,有多少程序中被测试过,不考虑成本跟投资效益比,涵盖率越高,代表 ...

  3. 自动生成Code First代码

    自动生成Code First代码 在前面的文章中我们提到Entity Framework的“Code First”模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Fram ...

  4. 第五次作业2、请将该code进行代码重构,使之模块化,并易于阅读和维护;

    1.请运行下面code,指出其功能: (需附运行结果截图,并用简短文字描述其功能) 显示了人的姓名.年龄 2.请将该code进行代码重构,使之模块化,并易于阅读和维护: 3.观看视频The Exper ...

  5. Code::Blocks代码自动提示设置及常用快捷键

    Code::Blocks代码自动提示设置及常用快捷键(适用windows和linux) 1)以下需要设置的地方均在Settings->Editor...弹出的对话框中. 2)不少命令都可针对当前 ...

  6. VS Code 用户自定义代码片段(React)

    VS Code 用户自定义代码片段(React) .jsxReact组件模板:javascriptreact.json { "Import React": { "pref ...

  7. dead code 死代码 无作用的代码

               DatasetVector datasetvector=(DatasetVector)dataset;           if (datasetvector == null) ...

  8. IDEA工具java开发之 常用插件 git插件 追加提交 Code Review==代码评审插件 撤销提交 撤销提交 关联远程仓库 设置git 本地操作

    ◆git 插件 请先安装git for windows ,git客户端工具 平时开发中,git的使用都是用可视化界面,git命令需要不时复习,以备不时之需 1.环境准备 (1)设置git (2)本地操 ...

  9. VS code调试代码快速上手必备知识

    一.通过简单的配置运行一个JavaScript程序 1.打开(创建)一个新的工作空间(文件夹),并创建一个js文件: var name='world'; var s='Hello,${name}!'; ...

随机推荐

  1. Android JNI编程(四)——C语言多级指针、数组取值、从控制台输入数组

    版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 一:前面我们介绍了一级指针的相关概念和用发,今天我们就来说一说多级指针. 1 ...

  2. php实现求数组中出现次数超过一半的数字(isset($arr[$val]))(取不同数看剩)(排序取中)

    php实现求数组中出现次数超过一半的数字(isset($arr[$val]))(取不同数看剩)(排序取中) 一.总结 1.if(isset($arr[$val])) $arr[$val]++; //1 ...

  3. Android中动态设置GridView的列数、列宽和行高

    在使用GridView时我们知道,列数是可以通过设计时的属性来设置的,列的宽度则是根据列数和GridView的宽度计算出来的.但是有些时候我们想实现列数是动态改变的效果,即列的宽度保持某个值,列的数量 ...

  4. 【u006】海战

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 在峰会期间,武装部队得处于高度戒备.警察将监视每一条大街,军队将保卫建筑物,领空将布满了F-2003飞 ...

  5. iOS 通讯录基础

    iOS  6 之后应用訪问通讯录须要获得用户授权,而且仅仅授权一次,即使卸载了之后再安装.也不用再次授权. 开发通讯录应用须要两个框架: 1.ABAddressBook    主要用于提供訪问通讯录中 ...

  6. UItextfield 动态限制输入的字数

    @property (nonatomic, strong) UITextField *txtName; - (void)viewDidLoad { [super viewDidLoad]; //UIC ...

  7. 51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp

    传送门 题目大意: 给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值. 题目分析: 为什么我觉得这道题最难的是贪心啊..首先要想到 在一条链上,深度大的编号要小 ...

  8. BZOJ 1260 - 区间dp

    Magic Door 题目大意: 给一个字符串,问需要至少覆盖多少次. 题目分析 区间dp: dp[i][j]表示达到i~j这个状态的最少覆盖次数,分两种情况: s[i] == s[j]: 此时内层可 ...

  9. 192M内存的VPS,安装Centos 6 minimal x86,无法安装node.js

    尝试了各种方法,始终安装不了node.偶然一次,安装了64位的Centos 6 minimal,竟然可以安装Node官网给出的命令安装node了,一切顺利.

  10. 【18.40%】【codeforces 631D】Messenger

    time limit per test 2 seconds memory limit per test 512 megabytes input standard input output standa ...