C#中的“按合同设计”

时间:2008-11-04 03:50:30

标签: c# design-by-contract

我想在我最新的C#应用​​程序中通过合同尝试一点设计,并希望语法类似于:

public string Foo()
{
    set {
        Assert.IsNotNull(value);
        Assert.IsTrue(value.Contains("bar"));
        _foo = value;
    }
}

我知道我可以从单元测试框架中获取这样的静态方法,但是我想知道这样的东西是否已经内置于该语言中,或者是否已经存在某种类型的框架。我可以编写自己的Assert函数,只是不想重新发明轮子。

11 个答案:

答案 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)

您可以使用尖锐架构的Design By Contract实施。这是链接:http://code.google.com/p/sharp-architecture/

此致

答案 7 :(得分:2)

尝试使用LinFu的DesignByContract库:

http://www.codeproject.com/KB/cs/LinFu_Part5.aspx

答案 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 languagesas 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;
    }
}