[英]'Design By Contract' in C#
我想在我最新的C#应用程序中通过契约尝试一点设计,并希望语法类似于:
public string Foo()
{
set {
Assert.IsNotNull(value);
Assert.IsTrue(value.Contains("bar"));
_foo = value;
}
}
我知道我可以从单元测试框架中获取这样的静态方法,但是我想知道这样的东西是否已经内置于该语言中,或者是否已经存在某种类型的框架。 我可以编写自己的Assert函数,只是不想重新发明轮子。
C#4.0代码合同
Microsoft已经在.net框架的4.0版本中发布了一个按合同设计的库。 该库最酷的功能之一是它还带有静态分析工具(类似于我猜的FxCop),它利用了您对代码签订的合同的详细信息。
以下是一些Microsoft资源:
以下是其他一些资源:
Spec#是一个流行的微软研究项目 ,它允许一些DBC结构,比如检查post和pre条件。 例如,二进制搜索可以使用前置条件和后置条件以及循环不变量来实现。 这个例子还有更多:
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的最佳方法。 通常,依赖于运行时断言会成为生产中的头痛,人们通常会选择使用异常 。
除了使用外部库,您在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”的类,它提供了检查前后条件的静态方法 。 我觉得这很整洁也很清楚。 它表达了我在代码中通过合同检查实现设计时所考虑的内容。
例如
public void Foo(Bar param)
{
Guard.ArgumentNotNull(param);
}
我认为通过合同检查表达设计是一种巧妙的方式。
试试LinFu的DesignByContract库:
对于我目前的项目(2010年2月,VS 2008),我选择http://lightcontracts.codeplex.com/
很简单,它只是运行时验证,没有任何奇怪的复杂性,你不需要从一些'奇怪的'基类派生,没有AOP,VS集成,它不适用于某些开发人员工作站等。
简单而复杂。
您可以查看nVentive Umbrella :
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");
}
}
}
}
我希望验证扩展是构建器,所以你可以做_foo = value.Validation().NotNull("Foo").Contains("bar").Value;
但事实就是如此(幸运的是它的开源,所以使它成为一个建设者是一个微不足道的变化)。
作为替代解决方案,您可以考虑进行域验证 。
最后, 作为Oslo的一部分, 新的M语言支持对其范围和字段的限制,这些限制既转换为T-SQL验证又转换为具有功能验证测试的CLR类(尽管Oslo在发布后已经很长时间了)。
最简单的方法,以及.NET Framework本身使用的方法是:
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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.