Должны ли условия повторяться при ссылке на другой контрактный метод? - PullRequest
2 голосов
/ 13 февраля 2012

Ниже приводится выдержка из договора об интерфейсе.

Мой вопрос: должен ли первый метод повторять предварительные условия второго метода? Поскольку контракты являются публичными, было бы приемлемо опустить повторение? В этом случае это просто ненулевая проверка, но я могу представить себе ситуацию, когда много кода повторяется и производительность снижается, поскольку идентичные проверки повторяются во время выполнения.

public int CommandConsumerCount(IWriteCommand writeCommand)
{
    Contract.Requires(writeCommand != null); // redundant?
    Contract.Requires(this.IsOwnerOf(writeCommand));

    Contract.Ensures(Contract.Result<int>() >= 0);

    return default(int);
}

public bool IsOwnerOf(IWriteCommand writeCommand)
{
    Contract.Requires(writeCommand != null);

    return default(bool);
}

Ответы [ 3 ]

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

Как правило, если метод A вызывает метод B, то A должен убедиться, что все контракты на B выполнены.

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

Например, если у вас есть:

CommandConsumerCount(x);

Без информации о x, тогда статическая проверка будет жаловаться, что IsOwnerOf требуется, чтобы быть верным. Чтобы доказать IsOwnerOf, ваш код должен сделать что-то вроде этого:

if (IsOwnerOf(x))
{
    CommandConsumerCount(x);
}

или

IWriteCommand GetWriteCommand()
{
    Contract.Ensures(IsOwnerOf(Contract.Result<IWriteCommand>()));

    //...
}

var x = GetWriteCommand();
CommandConsumerCount(x);

В обоих этих случаях также проверяется ненулевой контракт IsOwnerOf, и поэтому доказано, что аргумент не равен нулю.

Однако, если бы у вас было CommandConsumerCount быть примерно таким:

int CommandConsumerCount(IWriteCommand command)
{
    Contract.Requires(command != null);

    if (IsOwnerOf(command))
    {
       // ...
    }

    return 0;
}

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

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

Это зависит от того, является ли это условие (writeCommand != null в данном случае) обязательным как для метода CommandConsumerCount, так и для метода IsOwnerOf, или же оно является требованием только для метода IsOwnerOf.

Если это условие действительно требуется только для метода IsOwnerOf, то было бы нормально пропустить его из метода CommandConsumerCount.

Однако, если это условие требуется обоими методами, я придерживаюсь своего первоначального ответа ниже:

Я думаю, что, поскольку оба ваших метода общедоступны, требования контракта следует повторить. Если у вас был закрытый метод, который выполнял реальную работу, которую вызывали как метод IsOwnerOf, так и метод CommandConsumerCount (вместо метода CommandConsumerCount, вызывающего метод IsOwnerOf), то было бы хорошо пропустить вызов Contract.Requires внутри приватный метод.

Что касается производительности ... Меня не беспокоит влияние этих проверок на производительность, если только логика самих проверок не очень сложна. Вы можете настроить компилятор на исключение этих вызовов к Contract.Requires из скомпилированного вывода в разделе «Code Contracts» свойств проекта (при условии, что у вас установлен необходимый плагин).

Возможно, я упускаю суть вашего вопроса, хотя. Вы спрашиваете, нормально ли полностью опускать вызов IsOwnerOf внутри метода CommandConsumerCount? В этом случае я бы оставил вызов на месте, но если это касается производительности, я бы сконфигурировал проект так, чтобы исключить этот вызов для сборок Release, предполагая, что я выполнил достаточное тестирование со сборкой Debug, чтобы убедиться, что это условие выполняется. удовлетворены.

РЕДАКТИРОВАТЬ: после перечитывания вопроса становится ясно, что вы спрашиваете о проверке writeCommand != null, поэтому я вычеркнул вышеприведенный абзац.

Пример кода ниже добавления частного метода, который выполняет фактическую работу метода IsOwnerOf.

    // you may want to choose a different name for this method
    private bool _IsOwnerOf(IWriteCommand)
    {
        // actual work is done here in this private method
        return default(bool);
    }

    public bool IsOwnerOf(IWriteCommand writeCommand)
    {
        Contract.Requires(writeCommand != null);

        // call the private method to perform the actual work
        return _IsOwnerOf(writeCommand);
    }

    public int CommandConsumerCount(IWriteCommand writeCommand)
    {
        Contract.Requires(writeCommand != null);
        Contract.Requires(_IsOwnerOf(writeCommand)); // call the private _IsOwnerOf method instead of the public method

        Contract.Ensures(Contract.Result<int>() >= 0);

        return default(int);
    }
1 голос
/ 13 февраля 2012

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

Так что если CommandConsumerCount нужно, чтобы writeCommand не было нулевым, тоне является избыточным иметь контракт.

Однако, если единственная причина, по которой CommandConsumerCount необходимо writeCommand, чтобы не быть нулевым, - это передать его в IsOwnerOf, тогда это избыточно.

...