Почему этот контракт кода C # искажен? - PullRequest
4 голосов
/ 21 декабря 2009

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?);
}
}

EDIT

Лассе В. Карисен написал в комментариях:

Как насчет: Contract.Ensures (result == null || result> = 0);

Да, Карисен, я пробовал это раньше, и он компилируется. Но остается вопрос: нельзя ли иметь 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 ]

10 голосов
/ 02 апреля 2012

Контракт неверен, потому что все пункты контракта ДОЛЖНЫ появляться перед любым другим кодом.

2 голосов
/ 06 февраля 2012

Вам не нужно if, если вы хотите использовать логические манипуляции вместо использования подразумевает!

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);
...
1 голос
/ 21 декабря 2009

Просто догадываюсь. Возможно, это должно быть Contract.Ensures(result.Value >= 0)?

0 голосов
/ 12 июня 2016

Все вызовы «Обеспечивает и требует» должны быть перед всеми другими операторами в теле метода или свойства, включая простые назначения, которые вы используете, чтобы помочь читабельности.

Правильный синтаксис

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 или в Code Contracts , руководство .)

При использовании .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. Поскольку он имеет дело только с проверкой аргументов, он не поможет с вашим примером после условия.

0 голосов
/ 26 февраля 2016

Контракт имеет флаг условной компиляции. В релизе больше ты код

if condition
    contract
return

становится

if condition
   return

Вы видите проблему сейчас?

...