Плюсы / минусы различных методов тестирования предварительных условий?

17

Сверху моей головы я могу думать о 4 способах проверки нулевых аргументов:

Debug.Assert(context != null);
Contract.Assert(context != null);
Contract.Requires(context != null);
if (context == null) throw new ArgumentNullException("context");

Я всегда использовал последний метод, но я только что увидел фрагмент кода, который использовал Contract.Requires , с которым я не знаком. Каковы преимущества / недостатки каждого метода? Существуют ли другие способы?

В VS2010 w / Resharper,

  • Contract.Assert предупреждает меня, что выражение всегда верно (как он знает, я не совсем уверен ... не может HttpContext быть нулевым?),
  • Contract.Requires исчезает, и он говорит мне, что компилятор не будет вызывать метод (я предполагаю, что из-за прежней причины он никогда не будет равен нулю) и
  • Если я изменю последний метод на context != null , весь код, следующий за ним, исчезнет, ​​и он сообщает мне, что код эвристически недоступен.

Итак, кажется, что последние 3 метода имеют какой-то интеллект, встроенный в статическую проверку VS, а Debug.Assert просто тупой.

    
задан mpen 15.12.2010 в 02:19
источник
  • Вопрос не ясен. Вы на самом деле пытаетесь использовать их все сразу? –  Matthew Flaschen 15.12.2010 в 02:22
  • @ Матвей: Не знаю, как это не ясно ... нет, я не пытаюсь использовать их все сразу. Я сделал такой фрагмент кода, чтобы проиллюстрировать различные способы проверки нулевого значения. –  mpen 15.12.2010 в 03:59
  • Вот что я думал из остальной части вопроса, но синтаксическое представление, которое вы использовали, было странным. –  Matthew Flaschen 15.12.2010 в 04:06
  • Contract.Requires выделен серым цветом, поскольку он является условным символом CONTRACTS_FULL, который добавляется в компиляцию по кодовым контрактам, поэтому Resharper не видит этого. Если вы добавите CONTRACTS_FULL в свой проект, Resharper не будет его серым. –  porges 16.12.2010 в 09:44
  • Также: первое сообщение из Кодовых контрактов, второе из Resharper, и я не уверен в третьем - возможно, Resharper. Если вы используете Resharper с кодовыми контрактами, вы должны установить файл из этого вопроса: stackoverflow.com/questions/929859/... –  porges 16.12.2010 в 09:46

2 ответа

12

Я предполагаю, что к интерфейсу применяется контракт IHttpHandler.ProcessRequest , который требует этого контекста! = null. Контрактные интерфейсы наследуются их исполнителями, поэтому вам не нужно повторять Requires. Фактически, вам не разрешено добавлять дополнительные запросы Requires, так как вы ограничены требованиями, связанными с контрактом интерфейса.

Я думаю, что важно провести различие между указанием контрактного обязательства и просто выполнением нулевой проверки. Вы можете реализовать нулевую проверку и исключить исключение во время выполнения, чтобы сообщить разработчику, что они правильно используют ваш API. С другой стороны, выражение Контракта представляет собой действительно форму метаданных, которые могут быть интерпретированы контрактным переписывающим устройством (для представления исключений во время выполнения, которые были ранее реализованы вручную), а также статическим анализатором, который может использовать их для разбора о статической корректности вашего приложения.

Тем не менее, если вы работаете в среде, в которой вы активно используете Code Contracts и статический анализ, то, безусловно, предпочтительнее ставить утверждения в форме Контракта, чтобы воспользоваться статическим анализом. Даже если вы не используете статический анализ, вы все равно можете оставить дверь открытой для получения дополнительных преимуществ, используя контракты. Главное, на что следует обратить внимание, - настроить ли вы свои проекты для перезаписи, поскольку в противном случае контракты не приведут к исключениям времени выполнения, как вы могли бы ожидать.

Чтобы уточнить, что говорили комментаторы, разница между Assert, Assume и Requires:

  • Контрактное выражение выражения преобразуется в утверждение контрактного переписывающего устройства, и статический анализатор пытается доказать выражение на основе имеющихся доказательств. Если это невозможно проверить, вы получите предупреждение о статическом анализе.
  • Выражение Contract.Assume игнорируется контрактным переписывающим устройством (насколько мне известно), но интерпретируется статическим анализатором как новое доказательство, которое оно может принимать во внимание при его статическом анализе. Contract.Assume используется для «заполнения пробелов» в статическом анализе, либо там, где ему не хватает сложностей, чтобы делать необходимые выводы или когда они взаимодействуют с кодом, который не был украшен Контрактами, так что вы можете предположить, например , что конкретный вызов функции возвращает ненулевой результат.
  • Contract.Requires - это условия, которые всегда должны быть истинными при вызове метода. Они могут быть ограничениями на параметры для метода (которые являются наиболее типичными), а также могут быть ограничениями на публично видимые состояния объекта (например, вы можете разрешить вызов метода только в том случае, если Initialized is True.) Эти типы Ограничения позволяют пользователям вашего класса либо проверять инициализацию при использовании объекта (и, предположительно, обрабатывать ошибку соответствующим образом, если это не так), либо создавать свои собственные ограничения и / или инварианты классов, чтобы уточнить, что Инициализация действительно произошла.
ответ дан Dan Bryant 15.12.2010 в 02:24
  • Итак ... какая именно разница между Contract.Assert и Contract.Requires then? –  mpen 15.12.2010 в 04:05
  • @Ralph: Contract.Requires указывает на то, что должно быть истинным при вызове метода. Contract.Assert должен идти в середине вашего метода для проверки промежуточных состояний. Добавление контракта. Ассемблеры в правильных местах могут помочь статическому анализатору доказать правильность вашего кода, если он не может самостоятельно управлять им. –  Anon. 15.12.2010 в 04:18
  • На самом деле, статический контролер получал бы выгоду только от Contract.Assume. Этот метод ведет себя как Contract.Assert во время выполнения, но сообщает статическому контролеру, чтобы он не пытался статистически доказать это. –  koenmetsu 15.12.2010 в 08:53
  • Contract.Assert - это в основном просто документация для других программистов :) –  porges 16.12.2010 в 09:42
2

Первый метод подходит для тестирования нулевого условия, которое никогда не должно существовать. То есть, используйте его во время разработки, чтобы гарантировать, что он неожиданно не получит значение null. Поскольку обработка ошибок не выполняется, это не подходит для обработки нулевых условий в вашем выпущенном продукте.

Я бы сказал, что 2-я и 3-я версии аналогичны тем, что они никак не обрабатывают проблему.

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

    
ответ дан Jonathan Wood 15.12.2010 в 02:24
  • На самом деле, 2-й и 3-й методы используют статический анализ, чтобы попытаться доказать их истинность во время компиляции. –  Anon. 15.12.2010 в 02:33
  • @Anon: Но статический анализатор не сможет поймать все нулевые исключения во время компиляции ... что произойдет, если проскользнуть? Вызывает ли это исключение, сбой, ничего не делает или что? –  mpen 15.12.2010 в 04:03
  • @Ralph: Если статический анализатор не может что-то доказать (он не может показать, что в некоторых случаях контракт не удастся, но он не может доказать, что он всегда правильный), он покажет предупреждение компиляции ( не ошибка) и заменить его утверждением. –  Anon. 15.12.2010 в 04:19