"Дизайн по контракту" в С#
Я хотел попробовать небольшой дизайн по контракту в своем последнем приложении С# и хотел иметь синтаксис, похожий на:
public string Foo()
{
set {
Assert.IsNotNull(value);
Assert.IsTrue(value.Contains("bar"));
_foo = value;
}
}
Я знаю, что могу получить статические методы, подобные этому из структуры unit test, но я хотел знать, было ли что-то вроде этого уже встроено в язык, или если вокруг уже существует какая-то инфраструктура. Я могу написать свои собственные функции Assert, просто не хочу изобретать велосипед.
Ответы
Ответ 1
Контракты с кодом С# 4.0
Microsoft выпустила библиотеку для разработки по контракту в версии 4.0.net framework. Одной из самых крутых функций этой библиотеки является то, что она также поставляется со статическими инструментами анализа (аналогично FxCop, я думаю), которая использует детали контрактов, которые вы размещаете в коде.
Вот некоторые ресурсы Microsoft:
Вот некоторые другие ресурсы:
Ответ 2
SpeС# - популярный проект исследования Microsoft, который допускает некоторые конструкции DBC, такие как проверка сообщений и предварительных условий. Например, двоичный поиск может быть реализован с условием pre и post вместе с инвариантами цикла. Этот пример и многое другое:
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.
}
Обратите внимание, что использование языка SpeС# дает возможность проверять время компиляции для конструкций DBC, что для меня - лучший способ использовать DBC. Часто, полагаясь на утверждения времени исполнения, становится головной болью в производстве, и люди обычно предпочитают использовать исключения.
Есть другие языки, которые включают концепции DBC в качестве конструкций первого класса, а именно Eiffel, который также доступен для платформы .NET.
Ответ 3
Помимо использования внешней библиотеки, у вас есть простое утверждение в System.Diagnostics:
using System.Diagnostics
Debug.Assert(value != null);
Debug.Assert(value == true);
Не очень полезно, я знаю.
Ответ 4
Есть ответ в .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);
Ответ 5
http://www.codeproject.com/KB/cs/designbycontract.aspx
Ответ 6
Оглядываясь на код для Moq, я увидел, что они используют класс под названием Guard, который предоставляет статические методы для проверки условий pre и post, Я думал, что это было аккуратно и очень ясно. В нем выражается то, о чем я бы подумал при реализации дизайна по контракту в моем коде.
например.
public void Foo(Bar param)
{
Guard.ArgumentNotNull(param);
}
Я думал, что это был аккуратный способ выразить дизайн с помощью контрактных проверок.
Ответ 7
Вы можете использовать реализацию Design By Contract от четкой архитектуры. Вот ссылка: http://code.google.com/p/sharp-architecture/
Привет,
Лян
Ответ 8
Попробуйте библиотеку LinFu DesignByContract:
http://www.codeproject.com/KB/cs/LinFu_Part5.aspx
Ответ 9
Вы можете проверить 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");
}
}
}
}
Я хочу, чтобы расширения Validation были сборщиками, поэтому вы могли бы сделать _foo = value.Validation().NotNull("Foo").Contains("bar").Value;
, но это то, что есть (к счастью, его открытый источник, поэтому создание его строителя - это тривиальное изменение).
И в качестве альтернативного решения вы можете рассмотреть проверку домена.
Наконец, новые языки M, как часть Осло, поддерживают ограничения на их экстенты и поля, которые переводят как на проверку T-SQL, так и на класс CLR с функционирующими проверками валидации (хотя Осло долгое время отходит от выпуска).
Ответ 10
Для моего текущего проекта (февраль 2010, VS 2008) я выбрал http://lightcontracts.codeplex.com/
Простая, это просто проверка времени выполнения, без какой-либо странной сложности, вам не нужно выводить из некоторых "странных" базовых классов, без интеграции AOP, VS, которые не будут работать на некоторых рабочих станциях разработчиков и т.д.
Простота по сложности.
Ответ 11
Самый простой способ и способ, используемый в самой .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;
}
}