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

.NET Contracts-开源 .NET合同 立即下载

什么是契约

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

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.OldValue<int>(_words.Count) + 1 == _words.Count);
  5: 	CodeContract.EnsuresOnThrow
  
   (CodeContract.OldValue<
   int>(_words.Count) == _words.Count);

  
  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 <= Contract.Result<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 List<string> _words;
 12: 
 13:         public WordList(int capacity)
 14:         {
 15:             Contract.Requires(capacity > 0);
 16:             _words = new List<string>(capacity);
 17:         }
 18: 
 19:         public void Insert(string word)
 20:         {
 21:             Contract.Requires(word != null);
 22:             Contract.Ensures(Contract.OldValue<int>(_words.Count) + 1 == _words.Count);
 23:             Contract.EnsuresOnThrow
  
   (Contract.OldValue<
   int>(_words.Count) == _words.Count);

  
 24: 
 25:             int i;
 26:             for (i = 0; i < _words.Count; ++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 < _words.Count; ++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: }
大疆智图002:大疆智图倾斜摄影建模流程 本文主要介绍大疆智图倾斜摄影建模流程,包括新建工程、成果路径设置、导入照片、第一遍空三、像控刺点、第二遍空三、精度报告查看、三维重建等流程。 阅读详情

相关推荐

读取rosbag中的IMU信息并转为tum格式的csv和txt

rostopic -b xx.bag -p /xx > xxx.txt #for euroc structvio import os, sys,re,csv name="street_07" # basedir="/media/yinjie/2498AF1F98AEEF0C/adataset/15results/imu_bagtxt/"+name+'.txt' # txtdir="/media/yinjie/2498AF1F98AEEF0C//adataset/15results/imu_o

qq_42928559的博客 1919

Microsoft Frame Work 4.0 契约式编程

契约式编程不是一门崭新的编程方法论。C/C++ 时代早已有之。Microsoft 在 .NET 4.0 中正式引入契约式编程库。博主以为契约式编程是一种相当不错的编程思想,每一个开发人员都应该掌握。它不但可以使开发人员的思维更清晰,而且对于提高程序性能很有帮助。值得一提的是,它对于并行程序设计也有莫大的益处。 我们先看一段很简单的,未使用契约式编程的代码示例。 // .NET 代码示例 pu...

weixin_34253539的博客 297

利用Python实现FGO自动战斗脚本

Fate/Grand Order(非的肝不过欧的)作为索尼为了拯救自己不倒闭而开发的面向月厨的骗氪养成抽卡爆肝游戏,居然没有像隔壁《阴阳师》的自动战斗系统(看看别人现在都自带脚本了)。毕竟是懒得肝,就不妨写一个脚本来肝算了,省时省力。 开始 一个战斗脚本,其实可以算是AI的初步了。虽然只是暴力算出造成最大伤害的方案orz。 我们在这里不考虑释放 技能 、 宝具 和 暴击星 这三样非常重要的东西。...

web开发与Python 5485

c# .net接口协定Contract

1.背景 学习以及研究协定Contract 2.代码 (1)接口代码 [ContractClass(typeof(PersonContract))] public interface IPerson { string FirstName { get; set; } string LastName { get;set; } ...

连大北一,明哥 1556

双摄像头的实时视频拼接及目标跟踪(一)

本章主要讲图像拼接算法的研究 先膜拜一下知乎大佬全面的叙述:https://www.zhihu.com/question/34535199/answer/135169187 本文图像拼接是基于opencv的图像拼接,流程分为以下几步 ...

Y.W.jian的博客 3万+

STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统

STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统,记录配置过程

Jack的小窝 5942

C#源码分析—List

字段: private const int _defaultCapacity = 4; //默认容量 private T[] _items; //内部数组 [ContractPublicPropertyName("Count")] private int _...

weixin_43834106的博客 531

AI:Coze、Dify、n8n:三款低代码工具如何选?

Coze、Dify、n8n:三款低代码工具如何选?

Kant2048的博客 2805

C#代码协定Contract静态类

Contract 类 静态类Contract 参考文档: 代码协定Contract 命名空间: System.Diagnostics.Contracts 包含用于表示程序协定(如前置条件Preconditions、后置条件Postconditions 和对象固定Invariants)的静态方法。 注解 代码协定类使你可以在代码中指定前置条件、后置条件和对象固定。 前置条件是输入方法或属性时必须满足的要求。 后置条件描述在方法或属性代码退出时的预期。 对象固定条件描述了不存在任何...

ylq1045的专栏 1836

利用 SAP Joule 提升 ABAP 开发效率的前提条件与限制全解析

综上,想要在 ABAP Development Tools 中顺利使用 SAP Joule,既要准备好 BTP、IAS、ABAP 系统与 ADT 的版本与信任链,也要理解许可证、配额、语法范围等限制。只有在这些前提全部满足后,Joule 才能真正发挥生成式 AI 的价值,帮助 ABAP 开发者从繁琐模板中解放出来,把注意力投入在业务创新与 Clean Core 改造之上。未来 SAP 还将扩展中文语境、自动修复建议以及跨系统智能代理等功能,值得持续关注。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 661

Design By Contract(契约式设计)

1.前提 这是Bertrand Meyer在EL语言中提出的一个设计风格,流行了近20年。不过现在比较少用,不是很流行了,主要原因在下面提出   2.什么是契约式设计 契约式设计就是按照某种规定对一些数据等做出约定,如果超出约定,程序将不再运行,例如要求输入的参数必须满足某种条件 在scala中常用require和assume等 class BankAccount(val balanc

u013571243的专栏 3472

DPABISurf使用教材

严老师的官方教程:http://rfmri.org/Course DPABISurf版本:dpabi50docker.tar.gz MATLAB版本:2018b 一、数据准备 (1)如果是数据的DICOM格式,整理成以下格式(注意文件夹的命名方式): 如果觉得不是很清楚,可以在官网下载Demo数据(http://rfmri.org/DemoData),参照里面的文件格式进行整理即可。 (2)如果数据是nii格式,整理成以下格式: 这里没有使用官网的demo数据进行学习,而是..

从事脑科学核磁共振方法学研究,在Nature communications等权威期刊发表研究论文,熟练掌握磁共振处理方法和统计学方法,欢迎大家和我交流。 8514

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

来源:http://blog.csdn.net/ATField/archive/2009/08/20/4465227.aspx   .NET 4.0中的新功能介绍:契约式设计 (Design By Contracts) 收藏 什么是契约我们先来看一个很简单的例子:Void WordList.Insert(string word)这个函数负责将word以升序插入到WordList

gogo313的专栏 565

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

结对项目-------陈伯雄&&贾伟

通过这次结对编程我体会的结对编程的优点和缺点:优点:1、有利于提升项目质量,减少Bug;2、有利于知识传递,降低学习成本;3、多人熟悉同一段代码,减少项目风险;4、与别人一起工作,相互讨论,可能更快更有效地解决问题;5、个人承担的压力不会那么大。缺点:1、对于有不同编程习惯的人员来说,结对编程可能会造成麻烦;2、编程人员水平相差较大时,有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到...

andyli0808的博客 151

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

硬件I2C与软件I2C深度对比:从时序原理到实战选型避坑指南

I2C总线作为嵌入式系统中广泛使用的两线制通信协议,其硬件外设与软件模拟两种实现方式在时序控制、GPIO配置、中断处理及DMA配合等维度存在显著差异。硬件I2C依赖MCU内部状态机自动完成起始、地址发送与ACK检测,效率高但灵活性受限;软件I2C通过GPIO手动模拟时序,引脚任意映射、时序可定制,却占用CPU资源且易受中断干扰。理解开漏输出、时钟拉伸、总线死锁恢复等底层机制,是保障通信可靠性的关键。在STM32、GD32、CH32V307及嵌入式Linux等平台上,合理选择实现方式并设计异常恢复逻辑,直接影

weixin_34121282的博客 307

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

结对编程-电梯调度

11061176王洛书-11061148吴文会 一、结对编程 1.1结对编程的优点 结对编程相对于个人编程有很多优点。 首先,督促作用,在讨论过程中能够很快投入工作,为了不耽误对方时间,我们会尽快完成各自的任务; 第二,节约时间,相互交流,能够更快的确定算法方案,比起一个人苦思冥想要来的快得多; 第三,优化算法,两个人的想法结合到一起,取精华弃糟粕,得到双方均认可的算法; 第四...

weixin_30878361的博客 204

C#/.NET 中的契约

将文档放到代码里面,文档才会及时地更新! 微软从 .NET Framework 4.0 开始,增加了 System.Diagnostics.Contracts 命名空间,用来把契约文档融入代码。然而后面一直不冷不热,Visual Studio 都没天然支持。ReSharper 不知何时加入了 ReSharper Annotations,在 ReSharper 插件工作...

weixin_30607659的博客 500

9.Application.Contracts项目

愿你出走半生,归来仍是少年! 当前项目构成如下: 项目结构 1.Dto Dto作为应用服务的数据传输对象,连接应用与后台的数据交互。根据Abp框架的设定,一般的CrudAppService会具备5个Dto,分别如下: TGetOutputDto:用于Get接口的返回结果参数,实体详情。 TGetListOutputDto:用于GetList接口的返回结果参数,实体列表项。 TGetListInput:用于GetList接口的请求参数,需继承于PagedAndSorted...

我就是那个帕吉 944

Design by Contract(契约式设计)

DBC  元素 先验条件:  针对方法(method),它规定了在调用该方法之前必须为真的条件。 后验条件: 也是针对方法,它规定了方法顺利执行完毕之后必须为真的条件。 不变式:  针对整个类,它规定了该类任何实例调用任何方法都必须为真的条件。 DBC 六大原则 ...

分布式成长 617
上一篇: MSDN Webcast预告:.NET 4.0中的新特性系列课程(3):.NET 4.0 Beta1 Interop 新特性介绍 (Level 200)
下一篇: .NET 4.0新功能介绍:In Process Side By Side
ATField
博客等级 码龄25年 673粉丝 119原创
评论 22
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值