我想在我最新的C#应用程序中通过合同尝试一点设计,并希望语法类似于:
public string Foo()
{
set {
Assert.IsNotNull(value);
Assert.IsTrue(value.Contains("bar"));
_foo = value;
}
}
我知道我可以从单元测试框架中获取这样的静态方法,但是我想知道这样的东西是否已经内置于该语言中,或者是否已经存在某种类型的框架。我可以编写自己的Assert函数,只是不想重新发明轮子。
答案 0 :(得分:76)
C#4.0代码合同
Microsoft已经在.net框架的4.0版本中发布了一个按合同设计的库。该库最酷的功能之一是它还带有一个静态分析工具(类似于我猜的FxCop),它利用了你在代码上签订的合同的细节。
以下是一些Microsoft资源:
以下是其他一些资源:
答案 1 :(得分:22)
Spec#是一种流行的microsoft research project,允许使用某些DBC结构,例如检查帖子和前置条件。例如,二进制搜索可以使用前置条件和后置条件以及循环不变量来实现。 This example and more:
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的最佳方式。通常,依赖于运行时断言会成为生产中的头痛,人们通常会选择use exceptions。
有other languages将DBC概念作为第一类构造,即Eiffel,它也可用于.NET平台。
答案 2 :(得分:11)
除了使用外部库之外,您在System.Diagnostics中有一个简单的断言:
using System.Diagnostics
Debug.Assert(value != null);
Debug.Assert(value == true);
我知道不是很有用。
答案 3 :(得分:6)
.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);
答案 4 :(得分:3)
答案 5 :(得分:2)
Looking over the code for Moq I saw that they use a class called 'Guard' that provides static methods for checking pre and post conditions。我觉得这很整洁也很清楚。它表达了我在代码中通过合同检查实现设计时所考虑的内容。
e.g。
public void Foo(Bar param)
{
Guard.ArgumentNotNull(param);
}
我认为通过合同检查表达设计是一种巧妙的方式。
答案 6 :(得分:2)
答案 7 :(得分:2)
尝试使用LinFu的DesignByContract库:
答案 8 :(得分:1)
您可以查看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;
,但它就是它(幸运的是它的开源,所以使它成为一个构建器是一个微不足道的变化)。
作为替代解决方案,您可以consider domain validation。
最后,new M languages,as part of Oslo支持对其范围和字段的限制,这些限制转换为T-SQL验证和具有功能验证测试的CLR类(尽管奥斯陆很长时间没有发布)。
答案 9 :(得分:1)
对于我目前的项目(2010年2月,VS 2008),我选择http://lightcontracts.codeplex.com/
简单,它只是运行时验证,没有任何奇怪的复杂性,你不需要从一些'奇怪的'基类派生,没有AOP,VS集成,这将无法在某些开发人员工作站上工作,等等。
简单而复杂。
答案 10 :(得分:1)
最简单的方法,以及.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;
}
}