Почему этот контракт кода C# искажен?
Visual Studio показывает ошибку, когда я пишу этот контракт ниже.
Ошибка 20 Раздел неверного контракта в методе '....get_Page'
Проблема с блоком if?
public int? Page
{
get
{
int? result = Contract.Result<int?>();
if (result != null)
Contract.Ensures(result >= 0);
return default(int?);
}
}
РЕДАКТИРОВАТЬ:
Лассе В. Карисен написал в комментариях:
Как насчет: Contract.Ensures(result == null || result >= 0);?
Да Karisen, я пробовал это раньше, и он компилируется. Но остается вопрос: нельзя ли иметь ifs при использовании контрактов?
Другая проблема, с которой я столкнулся, заключается в том, что я ничего не понимаю (в основном, учитывая приведенный выше пример работ), также включает использование результата:
public int IndexOf(T item)
{
Contract.Assert(item != null);
Contract.Assert((item as IEntity).ID > 0);
int result = Contract.Result<int>();
Contract.Ensures(result >= -1);
return default(int);
}
5 ответов
Просто догадываюсь. Возможно, так и должно быть Contract.Ensures(result.Value >= 0)
?
Контракт искажен, потому что все пункты контракта ДОЛЖНЫ появляться перед любым другим кодом.
Вам не нужно, если, чтобы делать логические манипуляции вместо использования подразумевает!
public int? Page
{
get
{
Contract.Ensures( (result!= null).Implies(result >= 0) );
var result = ...
...
return result;
}
}
Также вы должны использовать Требуется не утверждать при тестировании аргументов метода и других предварительных условий.
public int IndexOf(T item)
{
Contract.Requires(item != null);
Contract.Requires((item as IEntity).ID > 0);
...
Все вызовы Ensures и Requires должны быть перед всеми остальными операторами в теле метода или свойства, это включает в себя простые назначения, которые вы используете, чтобы помочь читабельности.
Правильный синтаксис
public int? Page {
get {
Contract.Ensures(Contract.Result<int?>() == null
|| Contract.Result<int?>() >= 0);
return default(int?);
}
}
}
Это очень некрасиво, намного хуже, чем обычно if (x || y) throw new ArgumentOutOfRangeException()
,
Специальные атрибуты
Есть несколько окольный способ обойти это. ContractAbbreviatorAttribute
а также ContractArgumentValidatorAttribute
специальные атрибуты, которые ccrewrite
понимает, что делает вашу жизнь проще. (Для получения дополнительной информации см. System.Diagnostics.Contracts
документация по пространству имен в MSDN или руководство по контрактам.)
Если используется.NET 4 или более ранняя версия: эти атрибуты находятся в платформе, начиная с.NET 4.5, но для предыдущих версий вы можете получить исходный файл для них из каталога, в который устанавливается Code Contracts. (C:\Program Files (x86)\Microsoft\Contracts\Languages\
) В этой папке находятся CSharp
а также VisualBasic
подпапки, которые имеют ContractExtensions.cs
(или.vb) файл, содержащий требуемый код.
ContractAbbreviatorAttribute Этот атрибут позволяет эффективно создавать макросы контрактов. При этом свойство вашей страницы может быть записано так:
public int? Page {
get {
EnsuresNullOrPositive();
return default(int?)
}
}
[ContractAbbreviator]
static void EnsuresNullOrPositive(int? x) {
Contract.Ensures(
Contract.Result<int?>() == null ||
Contract.Result<int?>() >= 0);
}
EnsuresNullOrPositive
также может храниться в статическом классе и повторно использоваться в вашем проекте, или может быть опубликован и помещен в служебную библиотеку. Вы также можете сделать его более общим, как в следующем примере.
[ContractAbbreviator]
static void EnsuresNullOrPositive<Nullable<T>>(Nullable<T> obj) {
Contract.Ensures(
Contract.Result<Nullable<T>>() == null ||
Contract.Result<Nullable<T>>() >= default(T));
}
Для моей собственной служебной библиотеки у меня есть статический класс с именем Requires
и статический класс с именем Ensures
каждый со множеством статических методов, украшенных ContractAbbreviator
, Вот некоторые примеры:
public static class Requires {
[ContractAbbreviator]
public static void NotNull(object obj) {
Contract.Requires<ArgumentNullException>(obj != null);
}
[ContractAbbreviator]
public static void NotNullOrEmpty(string str) {
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(str));
}
[ContractAbbreviator]
public static void NotNullOrEmpty(IEnumerable<T> sequence) {
Contract.Requires<ArgumentNullException>(sequence != null);
Contract.Requires<ArgumentNullException>(sequence.Any());
}
}
public static class Ensures {
[ContractAbbreviator]
public static void NotNull(){
Contract.Ensures(Contract.Result<object>() != null);
}
}
Их можно использовать так:
public List<SentMessage> EmailAllFriends(Person p) {
Requires.NotNull(p); //check if object is null
Requires.NotNullOrEmpty(p.EmailAddress); //check if string property is null or empty
Requires.NotNullOrEmpty(p.Friends); //check if sequence property is null or empty
Ensures.NotNull(); //result object will not be null
//Do stuff
}
ContractArgumentValidatorAttribute Я не использовал этот вне учебных пособий, но в основном он позволяет вам написать пакет несколько if (test) throw new ArgumentException()
звонки в одном звонке, который ведет себя как звонок Contract.Requires
, Так как он имеет дело только с проверкой аргументов, он не поможет с вашим примером после условия.
Контракт имеет флаг условной компиляции. В релизе больше тебе кода
if condition
contract
return
становится
if condition
return
Вы видите проблему сейчас?