Должны ли условия повторяться при ссылке на другой контрактный метод?
Ниже приводится выдержка из контракта на интерфейс.
Мой вопрос: должен ли первый метод повторять предварительные условия второго метода? Поскольку контракты являются публичными, было бы приемлемо опустить повторение? В этом случае это просто ненулевая проверка, но я могу представить себе ситуацию, когда много кода повторяется и производительность снижается, поскольку идентичные проверки повторяются во время выполнения.
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 ответа
Это зависит от того, является ли это условие (writeCommand != null
в данном случае) является требованием как для метода CommandConsumerCount, так и для метода IsOwnerOf, или если это требование только для метода IsOwnerOf.
Если это условие действительно требуется только для метода IsOwnerOf, то было бы нормально пропустить его из метода CommandConsumerCount.
Однако, если это условие требуется обоими методами, я отвечаю на свой оригинальный ответ ниже:
Я думаю, что, поскольку оба ваших метода публичны, требования контракта следует повторить. Если у вас был закрытый метод, который выполнял реальную работу, которую вызывали как метод IsOwnerOf, так и метод CommandConsumerCount (вместо метода CommandConsumerCount, вызывающего метод IsOwnerOf), то было бы хорошо пропустить вызов Contract.Requires внутри этого приватный метод.
Что касается производительности... Меня не беспокоит влияние этих проверок на производительность, если только логика самих проверок не очень сложна. Вы можете настроить компилятор так, чтобы исключить эти вызовы для Contract.Requires из скомпилированного вывода в разделе "Контракты кода" свойств проекта (при условии, что у вас установлен необходимый плагин).
Возможно, я упускаю суть вашего вопроса, хотя. Вы спрашиваете, нормально ли полностью опускать вызов 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);
}
Как правило, если метод A вызывает метод B, то A должен убедиться, что все контракты на B выполнены.
Однако, если метод используется в Requ, он является избыточным, поскольку вызывающий метода должен подтвердить все требования.
Например, если у вас есть:
CommandConsumerCount(x);
Без информации о x
, тогда статическая проверка пожалуется что IsOwnerOf
требуется, чтобы быть правдой. Чтобы доказать IsOwnerOf
, ваш код должен сделать что-то вроде этого:
if (IsOwnerOf(x))
{
CommandConsumerCount(x);
}
или же:
IWriteCommand GetWriteCommand()
{
Contract.Ensures(IsOwnerOf(Contract.Result<IWriteCommand>()));
//...
}
var x = GetWriteCommand();
CommandConsumerCount(x);
В обоих этих случаях IsOwnerOf
Контракт not-null также будет проверен, и, таким образом, аргумент будет не нулевым.
Однако, если у вас было CommandConsumerCount
будь что-то вроде этого:
int CommandConsumerCount(IWriteCommand command)
{
Contract.Requires(command != null);
if (IsOwnerOf(command))
{
// ...
}
return 0;
}
В этом случае CommandConsumerCount
потребуется контракт, так как вызывающий не обязан доказывать, что IsOwnerOf
истина, и поэтому ненулевой контракт не будет проверен.
Я бы сказал, что каждый метод должен указывать только те условия, в которых он нуждается, и не знать о методах условий, которые он вызывает.
Так что если CommandConsumerCount
потребности writeCommand
чтобы не быть нулевым, то не является лишним иметь контракт.
Однако, если единственная причина CommandConsumerCount
потребности writeCommand
не быть нулевым, значит передать его IsOwnerOf
тогда это излишне.