`
lovnet
  • 浏览: 6704542 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

.NET 4.0中的新功能介绍:契约式设计 (Design By Contracts)

阅读更多

什么是契约

我们先来看一个很简单的例子:

Void WordList.Insert(string word)

这个函数负责将word以升序插入到WordList中的单词列表中,word不可以为NULL。

上面这些说明文字都是用来描述此函数的行为的。当使用该函数的调用者看到这些说明文字的时候,便知道函数应该如何调用以及在不同情况下的函数行为,换言之,上面这段说明文字简单的描述了函数调用者和被调用者的一种约定,这种约定也被称之为契约(Contracts)。契约一般来讲可以分成三类,包括:

1. Precondition:函数调用之前需要满足何种条件:比如,参数word不可以为NULL

2. Postcondition:函数调用之后需要满足何种条件:比如,参数word被加入到WordList的成员m_wordList中,m_wordList元素个数+1

3. Invariant:函数调用之前之后总是需要满足的条件是什么:比如,m_wordList中的单词总是以升序排列

契约式设计这个概念是Bertrand Meyer提出的,并在Eiffel Programming Language这本书中有详细的描述,Eiffel语言本身对契约式设计支持也非常好,有兴趣的朋友可以尝试并比较一下。

.NET 4.0中的Contracts

在.NET 4.0中引入了对契约式设计的支持,我们来看一下,如果上面那个例子用4.0中的契约式设计功能应该如何编写:

  1: public void WordList.Insert(string word)
  2: {
  3: 	CodeContract.Requires(word != null);
  4: 	CodeContract.Ensures(CodeContract.OldValueint>(_words.Count) + 1 == _words.Count);
  5: 	CodeContract.EnsuresOnThrow<applicationexception>(CodeContract.OldValueint&gt;(_words.Count) == _words.Count);
</applicationexception>
  6: 	…
  7: }
  8: 

其中:

1. Contract.Requires是Precondition

2. Contract.Ensures是PostCondition

3. Contract.EnsuresOnThrow是Postcondition,和Ensures的区别是这是在Throw的情况下需要满足的Postcondition

可以看到,Contracts在被显式的放在代码当中,而不仅仅是说明性的文字,那这样有什么好处呢?

1. 提供了运行时支持:这些Contracts都是可以被运行的,并且一旦条件不被满足,会弹出类似Assert的一样的对话框报错,如下:

clip_image002

2. 提供了静态分析支持:通过静态分析Contracts,静态分析工具可以比较容易掌握函数的各种有关信息,甚至可以作为Intellisense

看到这里,可能有些朋友会有一些疑问:Contracts可以做条件检查并且弹出类似Assert的对话框,这个和Assert有何区别呢?其实,Contracts和Assert的区别主要在于:

1. Contracts的意图更加清晰,通过不同的Requires/Ensures等等调用,代表不同类型的条件,比单纯的Assert更容易理解和进行自动分析

2. Contracts的位置更加统一,将3种不同条件都放在代码的开始处,而非散见在函数的开头和结尾,便于查找和分析。

3. 不同的开发人员、不同的小组、不同的公司、不同的库可能都会有自己的Assert,这就大大增加了自动分析的难度,也不利于开发人员编写代码。而Contracts直接被.NET 4.0支持,是统一的。

当然了,Contracts也和Assert有一些非常类似的地方,比如Contracts和Assert都可以运行时检查错误,也可以在随意的在代码中关闭打开。VS中支持Contracts的几种不同的典型配置:

1. CONTRACTS_FULL:打开所有Contracts

2. CONTRACTS_PRECONDITIONS:仅有Precondition

3. RequireAlways Only:仅有RequireAlways。RequireAlways的意思等会讲到

这些选项都可以通过项目的Code Contracts页面来进行修改,这个页面是通过安装Contracts工具包获得的:

clip_image004

在这里提醒大家一下,在使用Contracts功能之前,一定要下载最新版的Contracts开发工具包:http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx

这个工具包提供了一系列的Contracts所需要的一些工具,文档以及VS的插件。不安装这个工具包将无法使用Contracts的功能。

刚才我们谈到了Requires和Ensures两种条件,这里把.NET 4.0中的最常用的几种Contracts列一下:

1. Requires:函数入口处必须满足的条件

2. Ensures:函数出口处必须满足的条件

3. Invariants:所有成员函数出口处都必须满足的条件

4. Assertions:在某一点必须满足的条件

5. Assumptions:在某一点必然满足的条件,用来减少不必要的警告信息

其中,对于Invariant需要稍作一点说明。因为Invariant是需要对每个成员函数都需要起到作用,显然如果把这个条件放在每个函数的末尾处可以起到这个效果,但是这么做显得比较笨。.NET 4.0中采取的方式是这样的:使用某个成员函数作为Invariant,上面标记ContractInvariantMethod属性,然后在这个成员函数里面用Contracts.Invariant来指定每一条Invariant条件。这样,Invariant就会对每个函数起作用了。同时,这个Invariant方法也应该标记上PureAttribute属性,表明该函数不存在副作用(不会修改对象的状态)

Contracts的奥秘

看到这里,不知道有些朋友发现没有,不管是Ensures还是Invariant,它们的位置并不是代码应该所存在的位置。对于Ensures来讲,它是函数出口条件,那么必然在出口时候被调用,但是为什么Ensures写在前面呢?同样的,Invariant是对每个函数起作用,如果单独写一个函数作为Invariant怎么保证它会被每个函数调用到呢?其实这些都是很合理的问题。首先,Ensure和Invariant的这种写法是很合理的,原因之前也提到过了,剩下的问题是,.NET如何保证这些条件会在正确的时候被执行。其实,在编译的时候,Contracts的工具包中有一个小工具ccrewrite,这个工具负责将编译出来的二进制代码进行调整,如下:

clip_image006

可以看到,ccrewrite负责将各种条件的位置进行调整,最终使得条件处于正确的位置。

接口级别的契约

除了在方法实现中加入契约之外,接口也可以加入契约。接口首先需要加上一个ContractClassAttribute,指向对应的ContractClass:

  1: [ContractClass(typeof(IFooContract))]
  2: interface IFoo {
  3: 	int Count { get; }
  4: 	void Put(int value );
  5: }

而ContractClass则需声明ContractClassForAttribute,说明是IFoo的ContractClass,然后显式实现IFoo,并加入Contract:

  1: [ContractClassFor(typeof(IFoo))]
  2: sealed class IFooContract : IFoo {
  3: 	int IFoo.Count {
  4: 	get {
  5: 		Contract.Ensures( 0 int>() );
  6: 		return default( int ); // dummy return
  7: 	}
  8: }
  9: 
 10: void IFoo.Put(int value)
 11: {
 12: 	Contract.Requires( 0 value );
 13: }

一个完整的例子

现在我们来看一个完整的例子。在这个例子中WordList使用了Contract.Requires/Ensures/EnsuresOnThrow/Invariant等契约,还附送几个Bug,程序比较简单,这里就不多说了:

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.Diagnostics.Contracts;
  6: 
  7: namespace ContractDemo
  8: {
  9:     class WordList
 10:     {
 11:         private Liststring> _words;
 12: 
 13:         public WordList(int capacity)
 14:         {
 15:             Contract.Requires(capacity > 0);
 16:             _words = new Liststring>(capacity);
 17:         }
 18: 
 19:         public void Insert(string word)
 20:         {
 21:             Contract.Requires(word != null);
 22:             Contract.Ensures(Contract.OldValueint>(_words.Count) + 1 == _words.Count);
 23:             Contract.EnsuresOnThrow<applicationexception>(Contract.OldValueint&gt;(_words.Count) == _words.Count);
</applicationexception>
 24: 
 25:             int i;
 26:             for (i = 0; i 
 27:             {
 28:                 int compare = string.Compare(word, _words[i]);
 29:                 if (compare > 0)
 30:                     break;
 31:                 else if (compare == 0)
 32:                     throw new ApplicationException("Already exist!");
 33:             }
 34: 
 35:             _words.Insert(i, word);
 36:         }
 37: 
 38:         [ContractInvariantMethod()]
 39:         internal void Invariant()
 40:         {
 41:             // No null string
 42:             Contract.Invariant(Contract.ForAll(_words, w => w != null));
 43: 
 44:             // Make sure the words are in ascending order
 45:             Contract.Invariant(IsAscending());
 46:         }
 47: 
 48:         [Pure]
 49:         internal bool IsAscending()
 50:         {
 51:             bool isAscending = true;
 52:             for (int i = 1; i 
 53:             {
 54:                 if (string.Compare(_words[i - 1], _words[i]) > 0)
 55:                 {
 56:                     isAscending = false;
 57:                     break;
 58:                 }
 59:             }
 60: 
 61:             return isAscending;
 62:         }
 63:     }
 64: 
 65:     class Program
 66:     {
 67:         static void Main(string[] args)
 68:         {
 69:             WordList wordList = new WordList(0);
 70:             wordList.Insert("Hello");
 71:             wordList.Insert("World");
 72:         }
 73:     }
 74: }
分享到:
评论

相关推荐

    .Net.Framework3.5开发技术详解[中文][PDF][VOL1]

    许多技术人员在面对.NET Framework 3.0/3.5大量的新知识、新技术的时候感到彷徨,对自己现有的技术知识是否过时会产生疑问,对新技术会产生抵触的情绪,其实,只要能学习到真正代表业界趋势的技术,花不太多的时间...

    OPC .NET 3.0 (WCF) Contracts and ServerBase [1.20.3]

    OPC .NET 3.0 (WCF) Contracts and ServerBase [1.20.3]

    .Net.Framework3.5开发技术详解[中文][共二卷][PDF][VOL2]

    许多技术人员在面对.NET Framework 3.0/3.5大量的新知识、新技术的时候感到彷徨,对自己现有的技术知识是否过时会产生疑问,对新技术会产生抵触的情绪,其实,只要能学习到真正代表业界趋势的技术,花不太多的时间...

    OPC .NET 3.0 (WCF) Contracts and ServerBase

    资源来源于网络 OPC .NET 3.0 (WCF) Contracts and ServerBase 支持OPC Xi Server, 同时支持COM和WCF

    .Net编程工具CodeContracts.zip

    CodeContracts 是用于 .Net 程序中的开源工具。它可以作为你内部或者外部 API 的检查文档,并且可以通过实时监测、...CodeContracts 将基于合同设计(design-by-contract)的编程方式的优点全部带给了 .Net 编程语言。

    Essential Windows Communication Foundation For .NET Framework 3.5

    “Whether this is the first time or the fifty-first time you’re using WCF, you’ll learn something new by reading this book.” --Nicholas Allen, Program Manager, Web Services, Microsoft Windows ...

    .Net.Framework3.5开发技术详解

    第2章 .NET3.5 的新功能 2.1.NET3.5 概要 2.2 新的.NET基础类型 2.3 C#语言的强化 2.3.1 C#基本语法增强 2.3.2 LINQ(集成语言查询) 第3章 WindowsPresentationFoundation基础知识 3.1 ...

    Mastering Ethereum: Building Smart Contracts and DApps (EPUB)

    Mastering Ethereum: Building Smart Contracts and DApps (EPUB)

    c# 4.0 in nutshell 4th edition

    When you have questions about how to use C# 4.0 or the .NET CLR, this highly acclaimed bestseller has precisely the answers you need. Uniquely organized around concepts and use cases, this fourth ...

    Python库 | contracts_lib_py-0.2.3-py2.py3-none-any.whl

    资源分类:Python库 所属语言:Python 使用前提:需要解压 资源全名:contracts_lib_py-0.2.3-py2.py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    Microsoft Windows Communication Foundation 4.0 Cookbook for Developing SOA Applications

    The Windows Communication Foundation 4.0 (WCF 4.0) is a .NET-based application programming interface for building and running connected systems. It enables secure and reliable communication among ...

    WCF示例代码(C#.net)

    定义在该项目中的所有WCF服务实现了定义在Contracts中相应的服务契约,所以Services具有对Contracts项目的引用; •Hosting:一个控制台(Console)应用,实现对定义在Services项目中的服务的寄宿,该项目须要同时...

    kleisli-contracts:Kleisli gem 提供的 monad 合约 - https

    :合同为 gem 提供的 monad 提供以下: MaybeOf [ SomeType ]EitherOf [ LeftType , RightType ]ValidationOf [ FailureType , SuccessType ]安装将此行添加到应用程序的 Gemfile 中: gem 'kleisli-contracts' 然后...

    .NET Contracts-开源

    .NET合同

    pact:契约智能合约语言

    契约式编程语言 是一个开源的,Turing-在完整的智能合同文本已专门建造与blockchains第一考虑。 Pact致力于通过授权,数据管理和工作流中功能的最佳组合来促进事务逻辑。 阅读白皮书:有关其他信息,新闻和发展咨询...

    Wrox.Professional.WCF.4

    Written by a team of Microsoft MVPs and WCF experts, this book explains how the pieces of WCF 4.0 build on each other to provide a comprehensive framework to support distributed enterprise ...

    VB.NET Developer's Guide(4574).pdf

    made up by the author. 153_VBnet_TOC 8/16/01 1:13 PM Page xxii Contents xxiii Disconnected Layer 427 Using DataSet 428 Relational Schema 428 Collection of Tables 430 Data States 431 Populating ...

    Python库 | raiden-contracts-0.8.0.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:raiden-contracts-0.8.0.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    Python库 | plonehrm.contracts-2.0.3.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:plonehrm.contracts-2.0.3.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    Plasma- Scalable Autonomous Smart Contracts.pdf

    Plasma is a proposed framework for incentivized and enforced execution of smart contracts which is scalable to a significant amount of state updates per second (poten- tially billions) enabling the ...

Global site tag (gtag.js) - Google Analytics