内容简介:将文档放到代码里面,文档才会及时地更新!微软从 .NET Framework 4.0 开始,增加了
将文档放到代码里面,文档才会及时地更新!
微软从 .NET Framework 4.0 开始,增加了 System.Diagnostics.Contracts
命名空间,用来把契约文档融入代码。然而后面一直不冷不热,Visual Studio 都没天然支持。
ReSharper 也提供了 ReSharper Annotations,在 ReSharper 插件工作的情况下能够进行静态契约的验证。
C#8.0 的可空引用类型是 Roslyn 对 null 的验证,这个可能更加强大,既可以是编译警告,也可以是编译错误。
契约式编程
当你调用某个类库里面的方法时,你如何能够知道传入的参数是否符合规范?如何能够知道方法调用结束之后是否要对结果进行判断?
T DoSomething<T>(T parent) where T : class;
▲ 对于上面的方法,你知道 null
传入参数是合理的吗?返回的参数需要判空吗?
代码的编写者可能是这么写的:
public T DoSomething<T>(T parent) where T : class { if (parent == null) { throw new ArgumentNullException(nameof(parent)); } // 后续逻辑。 }
有些静态代码检查 工具 也许可以根据这里的参数判断代码块来认定为此处的参数不能为 null,但这种判断代码无处不在,静态检查工具如何能够有效地捕获每一处的检查呢?难道我们真的要去翻阅文档吗?然而除非是专门提供 SDK 的团队,否则文档通常都会滞后于代码,那么对于这些契约的修改可能就不太准确。
于是,契约式编程就应运而生。
它将前置条件(Precondition)、后置条件(Postcondition)、不变量(Invariant)等代码分离出来,按照特定的格式编写以便能够被静态检查工具分析出来。
有了静态分析工具以及契约代码的帮助,Visual Studio 的智能感知提示将能够直接告诉我们代码编写的潜在问题,而不必等到运行时再抛出异常,那时将降低开发效率,将增加生产环境运行的风险。
几种不同的契约方法
ReSharper Annotations
ReSharper 并没有将其称之为“契约”,因为它真的只是“文档级别”的约束,只会在写代码的时候具备一定程度的静态分析能力以便给出提示,并不提供运行时的检查。不过,ReSharper 会为我们生成运行时检查的代码。只要是装了 ReSharper 插件并用它写过代码的,应该都见过 ReSharper Annotations 了,因为它会在我们试图添加契约代码时自动添加契约标记(Attribute)。
▲ 生成 ReSharper Annotations
如果错过了首次提示,可以在 ReSharper 的设置界面中生成 Annotations 的代码。(复制一份代码然后新建一个文件粘贴。)
▲ 手动生成 ReSharper Annotations
ReSharper 中常用的契约 Attribute
- CanBeNull
- 表示参数或返回值可能为
null
。
- 表示参数或返回值可能为
- CannotApplyEqualityOperator
- 表示某个类型的相等比较不应该用
==
或!=
,而应该用Equals
。
- 表示某个类型的相等比较不应该用
- ItemCanBeNull
- 表示集合参数或集合返回值里某一项可能为
null
。 - 或者表示
Task<T>
返回值中的T
可能为null
。
- 表示集合参数或集合返回值里某一项可能为
- ItemNotNull
- 表示集合参数或集合返回值里每一项都不为
null
。 - 或者表示
Task<T>
返回值中的T
不为null
。
- 表示集合参数或集合返回值里每一项都不为
- LinqTunnel
- 表示某个方法就像 linq 方法一样。
- LocalizationRequired
- 表示参数字符串需要被本地化。
- NotNull
- 表示参数或返回值不可能为 null。
- PathReference
- 表示参数字符串是一个路径。
- Pure
- 表示方法不会修改任何状态(这意味着如果连返回值都不用,那调用了也相当于什么都没做)。
- RegexPattern
- 表示参数字符串是一个正则表达式(会被 ReSharper 代码着色)。
-
还有 100+ 个……
- ContractAnnotation
- 详见 Contract Annotations - Help - ReSharper ,可以使用约定的语法写出更复杂的契约。
我的朋友 林德熙 在 使用 Resharper 特性 一文中有这些契约对编写代码的更详细的效果描述和截图。
System.Diagnostics.Contracts
此命名空间下的 Contract
类型定义了几个方法,覆盖了我们编写一个方法所要遵循的契约模式。
private T DoSomething<T>(T parent) where T : class { // * 要开始此任务必须先满足某些条件(Requires,RequiresAlways,EndContractBlock) // 做一些操作。 // * 此时认定一定满足某个条件(Assume) // 继续执行一些操作。 // * 操作执行完后一定满足某组条件(Ensures,EnsuresOnThrows) }
以上代码中,星号(*)表示契约代码,其他表示方法内的普通代码。一个典型的例子如以下代码所示:
private T DoSomething<T>(T parent) where T : class { // * 要开始此任务必须先满足某些条件(Requires,EndContractBlock) Contract.Requires<ArgumentNullException>(parent != null); // 做一些操作。 // * 此时认定一定满足某个条件(Assume) Contract.Assume(parent != null); // 继续执行一些操作。 // * 操作执行完后一定满足某组条件(Ensures,EnsuresOnThrows) Contract.EnsuresOnThrow<InvalidOperationException>(Value != null); }
在这里, Requires
是真的会抛出异常的,但 Assume
和 EnsuresOnThrow
是需要写条件编译符为 CONTRACTS_FULL
的。
或者,这样用普通的抛异常的方式。如果使用普通方式抛出异常,需要遵循 if-then-throw
的模式,即有问题立刻就抛出异常。例如下面对 null
的判断就符合这样的模式。
private T DoSomething<T>(T parent) where T : class { // * 要开始此任务必须先满足某些条件(Requires,EndContractBlock) if (parent == null) throw new ArgumentNullException(nameof(parent)); Contract.EndContractBlock(); // 做一些操作。 // * 此时认定一定满足某个条件(Assume) Contract.Assume(parent != null); // 继续执行一些操作。 // * 操作执行完后一定满足某组条件(Ensures,EnsuresOnThrows) Contract.EnsuresOnThrow<InvalidOperationException>(Value != null); }
当然也可以不止是这样简单的判断,也可以调用其他方法,但要求方法必须是 [Pure]
方法,即方法执行完之后,除了返回一个值之外,不改变应用程序的任何状态。
对此契约的静态分析微软有提供工具: Microsoft/CodeContracts: Source code for the CodeContracts tools for .NET ,ReSharper 对此也有一丁点儿的支持。
Roslyn
Roslyn 相比于任何第三方契约的优势在于它甚至能在语法层面形成契约( 比如 C#8.0 中的可空引用类型 )。
实际应用
事实上在 GitHub 中,使用各种契约的都有,不过以 ReSharper Annotations 和 System.Diagnostics.Contracts 的居多;C#8.0 的可空引用类型等到 8.0 发布以后再看吧。
在实际应用中,并没有严格的说哪一个更好哪一个一般,两者都可以用,只要我们有分析和提示此契约的工具,就可以在项目中推行开来。
但是, 基于契约编写代码的模式却能帮助我们写出更加健壮的代码来 。也就是说,用哪个并不重要,重要的是—— 用起来 !
参考资料
以上所述就是小编给大家介绍的《C#/.NET 中的契约式编程,以及 ReSharper 为我们提供的契约特性》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 微服务环境下的集成测试探索(二):契约式测试
- 契约测试:解决微服务测试的问题
- 以模型为中心,携程契约系统的演进
- [ Laravel 5.7 文档 ] 底层原理 —— 契约(Contracts)
- 微服务下的契约测试 (CDC) 解读
- 如何多人守护一份数字契约?解析多方门限签名的妙用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
An Introduction to Genetic Algorithms
Melanie Mitchell / MIT Press / 1998-2-6 / USD 45.00
Genetic algorithms have been used in science and engineering as adaptive algorithms for solving practical problems and as computational models of natural evolutionary systems. This brief, accessible i......一起来看看 《An Introduction to Genetic Algorithms》 这本书的介绍吧!