繁体   English   中英

C#中的“按合同设计”

[英]'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的最佳方法。 通常,依赖于运行时断言会成为生产中的头痛,人们通常会选择使用异常

还有其他语言将DBC概念作为第一类构造包含在内,即Eiffel也可用于.NET平台。

除了使用外部库,您在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);
} 

我认为通过合同检查表达设计是一种巧妙的方式。

您可以使用尖锐架构的“按合同设计”实施。 以下是链接: http//code.google.com/p/sharp-architecture/

问候,

试试LinFu的DesignByContract库:

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

对于我目前的项目(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM