简体   繁体   English

C#中的“按合同设计”

[英]'Design By Contract' in C#

I wanted to try a little design by contract in my latest C# application and wanted to have syntax akin to: 我想在我最新的C#应用​​程序中通过契约尝试一点设计,并希望语法类似于:

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

I know I can get static methods like this from a unit test framework, but I wanted to know if something like this was already built-in to the language or if there was already some kind of framework floating around. 我知道我可以从单元测试框架中获取这样的静态方法,但是我想知道这样的东西是否已经内置于该语言中,或者是否已经存在某种类型的框架。 I can write my own Assert functions, just don't want to reinvent the wheel. 我可以编写自己的Assert函数,只是不想重新发明轮子。

C# 4.0 Code Contracts C#4.0代码合同

Microsoft has released a library for design by contract in version 4.0 of the .net framework. Microsoft已经在.net框架的4.0版本中发布了一个按合同设计的库。 One of the coolest features of that library is that it also comes with a static analysis tools (similar to FxCop I guess) that leverages the details of the contracts you place on the code. 该库最酷的功能之一是它还带有静态分析工具(类似于我猜的FxCop),它利用了您对代码签订的合同的详细信息。

Here are some Microsoft resources: 以下是一些Microsoft资源:

Here are some other resources: 以下是其他一些资源:

Spec# is a popular microsoft research project that allows for some DBC constructs, like checking post and pre conditions. Spec#是一个流行的微软研究项目 ,它允许一些DBC结构,比如检查post和pre条件。 For example a binary search can be implemented with pre and post conditions along with loop invariants. 例如,二进制搜索可以使用前置条件和后置条件以及循环不变量来实现。 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.
 }

Note that using the Spec# language yields compile time checking for DBC constructs, which to me, is the best way to take advantage of DBC. 请注意,使用Spec#语言会产生DBC构造的编译时检查 ,对我来说,这是利用DBC的最佳方法。 Often, relying on runtime assertions becomes a headache in production and people generally elect to use exceptions instead. 通常,依赖于运行时断言会成为生产中的头痛,人们通常会选择使用异常

There are other languages that embrace DBC concepts as first class constructs, namely Eiffel which is also available for the .NET platform. 还有其他语言将DBC概念作为第一类构造包含在内,即Eiffel也可用于.NET平台。

Aside from using an external library, you have a simple assert in System.Diagnostics: 除了使用外部库,您在System.Diagnostics中有一个简单的断言:

using System.Diagnostics

Debug.Assert(value != null);
Debug.Assert(value == true);

Not very useful, I know. 我知道,不是很有用。

There has an answer in .net Fx 4.0: .net Fx 4.0中有一个答案:

System.Diagnostics.Contracts System.Diagnostics.Contracts

http://msdn.microsoft.com/en-us/library/dd264808.aspx 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);

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 . 查看Moq的代码,我看到他们使用了一个名为“Guard”的类,它提供了检查前后条件的静态方法 I thought that was neat and very clear. 我觉得这很整洁也很清楚。 It expresses what I'd be thinking about when implementing design by contract checks in my code. 它表达了我在代码中通过合同检查实现设计时所考虑的内容。

eg 例如

public void Foo(Bar param)
{
   Guard.ArgumentNotNull(param);
} 

I thought it was a neat way to express design by contract checks. 我认为通过合同检查表达设计是一种巧妙的方式。

You can use a Design By Contract implementation from sharp-architecture. 您可以使用尖锐架构的“按合同设计”实施。 Here is the link: http://code.google.com/p/sharp-architecture/ 以下是链接: http//code.google.com/p/sharp-architecture/

Regards, 问候,

Liang

Try LinFu's DesignByContract Library: 试试LinFu的DesignByContract库:

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

For my current project (february 2010, VS 2008) I've choose http://lightcontracts.codeplex.com/ 对于我目前的项目(2010年2月,VS 2008),我选择http://lightcontracts.codeplex.com/

Simple, it's just runtime validation, without any weird complexity, you don't need to derive from some 'strange' base classes, no AOP, VS integration which won't work on some developer workstations, etc. 很简单,它只是运行时验证,没有任何奇怪的复杂性,你不需要从一些'奇怪的'基类派生,没有AOP,VS集成,它不适用于某些开发人员工作站等。

Simplicity over complexity. 简单而复杂。

You may want to check out nVentive Umbrella : 您可以查看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");
            }
        }
    }
}

I wish the Validation extensions were builders so you could do _foo = value.Validation().NotNull("Foo").Contains("bar").Value; 我希望验证扩展是构建器,所以你可以做_foo = value.Validation().NotNull("Foo").Contains("bar").Value; but it is what it is (fortunately its open source so making it a builder is a trivial change). 但事实就是如此(幸运的是它的开源,所以使它成为一个建设者是一个微不足道的变化)。

And as an alternative solution you could consider domain validation . 作为替代解决方案,您可以考虑进行域验证

Finally the new M languages , as part of Oslo , support restrictions on their extents and fields which translate both to T-SQL validation and a CLR class with functioning validation tests (though Oslo is a long time off from release). 最后, 作为Oslo的一部分, 新的M语言支持对其范围和字段的限制,这些限制既转换为T-SQL验证又转换为具有功能验证测试的CLR类(尽管Oslo在发布后已经很长时间了)。

The most straightforward way, and the way used in the .NET Framework itself, is to do: 最简单的方法,以及.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