C#中的“Design By Contract”
我想在我最新的C#应用程序中通过契约来尝试一些小devise,并且想要类似于以下的语法:
public string Foo() { set { Assert.IsNotNull(value); Assert.IsTrue(value.Contains("bar")); _foo = value; } }
我知道我可以从unit testing框架中获得这样的静态方法,但是我想知道这样的东西是否已经embedded到语言中,或者是否已经有某种框架在浮动。 我可以写我自己的断言function,只是不想重新发明轮子。
C#4.0代码合同
微软公司在.net框架4.0版本中发布了一个合同devise库。 该库最酷的function之一就是它还附带了一个静态分析工具(类似于我猜的FxCop),它利用了你放在代码上的合同的细节。
以下是一些Microsoft资源:
- 微软研究院的主要网站
- 用户手册
- 2008年PDC演示文稿
- 2009年PDC演示文稿
以下是其他一些资源:
- .NET 4.0代码合同 – 规范#活跃
- .NET代码合同和TDD是互补的
- 代码合同入门 – 第5部分:利用对象不variables
- 代码合同入门 – 第6部分界面合同
规范#是一个stream行的微软研究项目 ,允许一些DBC构造,如检查后和预条件。 例如,二分查找可以与循环不变式一起用前后条件来实现。 这个例子和更多:
public static int BinarySearch(int[]! a, int key) requires forall{int i in (0: a.Length), int j in (i: a.Length); a[i] <= a[j]}; ensures 0 <= result ==> a[result] == key; ensures result < 0 ==> forall{int i in (0: a.Length); a[i] != key}; { int low = 0; int high = a.Length - 1; while (low <= high) invariant high+1 <= a.Length; invariant forall{int i in (0: low); a[i] != key}; invariant forall{int i in (high+1: a.Length); a[i] != key}; { int mid = (low + high) / 2; int midVal = a[mid]; if (midVal < key) { low = mid + 1; } else if (key < midVal) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found. }
请注意,使用Spec#语言会产生DBC结构的编译时检查 ,对我而言,这是利用DBC的最佳方法。 通常,依靠运行时断言成为生产中的头痛问题,人们通常select使用exception 。
还有其他语言将DBC概念作为第一级构造,即也可用于.NET平台的Eiffel 。
除了使用外部库之外,在System.Diagnostics中还有一个简单的断言:
using System.Diagnostics Debug.Assert(value != null); Debug.Assert(value == true);
不是很有用,我知道。
.net Fx 4.0有一个答案:
System.Diagnostics.Contracts
http://msdn.microsoft.com/en-us/library/dd264808.aspx
Contract.Requires(newNumber > 0, “Failed contract: negative”); Contract.Ensures(list.Count == Contract.OldValue(list.Count) + 1);
查看Moq的代码,我看到他们使用了一个名为“Guard”的类,它提供了检查前后条件的静态方法 。 我认为这很整齐,很清楚。 它expression了我在代码中通过契约检查执行devise时会考虑的内容。
例如
public void Foo(Bar param) { Guard.ArgumentNotNull(param); }
我认为这是一个很好的方式来expressiondevise合同检查。
您可以使用锐利架构的“按合同devise”实施。 这里是链接: http : //code.google.com/p/sharp-architecture/
问候,
梁
试试LinFu的DesignByContract库:
你可能想看看nVentive伞 :
using System; using nVentive.Umbrella.Validation; using nVentive.Umbrella.Extensions; namespace Namespace { public static class StringValidationExtensionPoint { public static string Contains(this ValidationExtensionPoint<string> vep, string value) { if (vep.ExtendedValue.IndexOf(value, StringComparison.InvariantCultureIgnoreCase) == -1) throw new ArgumentException(String.Format("Must contain '{0}'.", value)); return vep.ExtendedValue; } } class Class { private string _foo; public string Foo { set { _foo = value.Validation() .NotNull("Foo") .Validation() .Contains("bar"); } } } }
我希望validation扩展是build设者,所以你可以做_foo = value.Validation().NotNull("Foo").Contains("bar").Value;
但它是这样(幸运的是它的开源,使它成为一个build设者是一个微不足道的变化)。
作为替代解决scheme,您可以考虑域validation 。
最后, 作为奥斯陆的一部分, 新的M语言支持对其范围和字段的限制,这些语言既可以转换为T-SQLvalidation,也可以通过functionvalidationtesting转换为CLR类(尽pipe奥斯陆的发布时间很长)。
对于我目前的项目(2010年2月,VS 2008),我select了http://lightcontracts.codeplex.com/
简单的说,这只是运行时validation,没有任何奇怪的复杂性,你不需要从一些“奇怪”的基类派生,没有AOP,VS集成,这将不能在一些开发工作站上工作,等等。
简单复杂。
最直接的方式,以及在.NET框架中使用的方式是:
public string Foo() { set { if (value == null) throw new ArgumentNullException("value"); if (!value.Contains("bar")) throw new ArgumentException(@"value should contain ""bar""", "value"); _foo = value; } }