«Дизайн по контракту» в C # - PullRequest
35 голосов
/ 04 ноября 2008

Я хотел попробовать немного дизайна по контракту в моем последнем приложении на C # и хотел иметь синтаксис, похожий на:

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

Я знаю, что могу получить подобные статические методы из фреймворка для юнит-тестирования, но я хотел знать, было ли что-то подобное уже встроено в язык или уже существовала какая-то фреймворк. Я могу написать свои собственные функции Assert, но не хочу изобретать велосипед.

Ответы [ 11 ]

76 голосов
/ 06 ноября 2008

C # 4.0 Кодовые контракты

Microsoft выпустила библиотеку для проектирования по контракту в версии 4.0 .net framework. Одна из самых классных возможностей этой библиотеки - это то, что она также поставляется с инструментами статического анализа (похожими на FxCop, я думаю), которые используют детали контрактов, которые вы заключаете в коде.

Вот некоторые ресурсы Microsoft:

Вот некоторые другие ресурсы:

22 голосов
/ 04 ноября 2008

Spec # - это популярный исследовательский проект Microsoft , который допускает некоторые конструкции DBC, такие как проверка после и предварительных условий. Например, двоичный поиск может быть реализован с предварительными и последующими условиями вместе с инвариантами цикла. Этот пример и другие:

 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.

11 голосов
/ 04 ноября 2008

Помимо использования внешней библиотеки, у вас есть простое утверждение в System.Diagnostics:

using System.Diagnostics

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

Не очень полезно, я знаю.

6 голосов
/ 01 июля 2010

Есть ответ в .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);
3 голосов
/ 04 ноября 2008
2 голосов
/ 18 декабря 2008

Попробуйте библиотеку DesignByContract от LinFu:

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

2 голосов
/ 13 ноября 2008

Вы можете использовать реализацию Design By Contract от Sharp-Architecture. Вот ссылка: http://code.google.com/p/sharp-architecture/

С уважением,

Liang

2 голосов
/ 04 ноября 2008

Просматривая код для Moq, я увидел, что они используют класс 'Guard', который предоставляет статические методы для проверки до и после условий . Я думал, что это было аккуратно и очень ясно. Он выражает то, о чем я буду думать, когда реализую дизайн с помощью контрактных проверок в моем коде.

, например

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

Я подумал, что это отличный способ выразить дизайн с помощью контрактных проверок.

1 голос
/ 04 марта 2011

Самый простой способ, используемый в самой .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;
    }
}
1 голос
/ 26 февраля 2010

Для моего текущего проекта (февраль 2010, VS 2008) я выбрал http://lightcontracts.codeplex.com/

Просто, это просто проверка во время выполнения, без каких-либо странных сложностей, вам не нужно извлекать уроки из «странных» базовых классов, без AOP, интеграции VS, которая не будет работать на некоторых рабочих станциях разработчиков и т. Д.

Простота над сложностью.

...