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

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

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

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


В VS2010 с Resharper,

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

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


person mpen    schedule 15.12.2010    source источник
comment
Вопрос не ясен. Вы действительно пытаетесь использовать их все сразу?   -  person Matthew Flaschen    schedule 15.12.2010
comment
@Matthew: Не знаю, почему это непонятно... нет, я не пытаюсь использовать их все сразу. Я сделал такой фрагмент кода, чтобы проиллюстрировать различные способы проверки на нуль.   -  person mpen    schedule 15.12.2010
comment
Это то, что я подумал из остальной части вопроса, но представление синтаксиса, которое вы использовали, было странным.   -  person Matthew Flaschen    schedule 15.12.2010
comment
@Matthew: использовал его, чтобы дать контекст. Чтобы люди знали, что такое context и где он используется, хотя это и не имеет особого отношения к вопросу. Думаю, я мог бы поставить сигнатуру функции 4 раза, но это было слишком много печатать и тратить слишком много места: P   -  person mpen    schedule 15.12.2010
comment
Contract.Requires неактивен, потому что он зависит от символа CONTRACTS_FULL, который добавляется в компиляцию с помощью Code Contracts, поэтому Resharper его не видит. Если вы добавите CONTRACTS_FULL в свой проект, Resharper не сделает его серым.   -  person porges    schedule 16.12.2010
comment
Также: первое сообщение от Code Contracts, второе от Resharper, и я не уверен насчет третьего — возможно, от Resharper. Если вы используете Resharper с кодовыми контрактами, вам следует установить файл из этого вопроса: stackoverflow.com/questions/929859/   -  person porges    schedule 16.12.2010


Ответы (2)


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

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

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


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

  • Выражение Contract.Assert преобразуется в утверждение переписателем контракта, и статический анализатор пытается доказать выражение на основе его существующих свидетельств. Если это не может быть доказано, вы получите предупреждение статического анализа.
  • Выражение Contract.Assume игнорируется переписателем контракта (насколько мне известно), но интерпретируется статическим анализатором как новое доказательство, которое он может принять во внимание в своем статическом анализе. Contract.Assume используется для «заполнения пробелов» в статическом анализе либо там, где ему не хватает сложности, чтобы сделать необходимые выводы, либо при взаимодействии с кодом, который не был украшен контрактами, так что вы можете, например, предположить , что конкретный вызов функции возвращает ненулевой результат.
  • Contract.Requires — это условия, которые всегда должны быть истинными при вызове вашего метода. Они могут быть ограничениями параметров метода (что является наиболее типичным), а также ограничениями общедоступных состояний объекта (например, вы можете разрешить вызов метода только в том случае, если Initialized имеет значение True). ограничений побуждают пользователей вашего класса либо проверять Initialized при использовании объекта (и, предположительно, обрабатывать ошибку соответствующим образом, если это не так), либо создавать свои собственные ограничения и/или инварианты класса, чтобы уточнить, что Initialization действительно произошла.
person Dan Bryant    schedule 15.12.2010
comment
Итак... в чем же тогда разница между Contract.Assert и Contract.Requires? - person mpen; 15.12.2010
comment
@Ralph: Contract.Requires указывает на вещи, которые должны быть истинными при вызове метода, Contract.Assert должен идти в середине вашего метода для проверки промежуточных состояний. Добавление Contract.Assert в нужных местах может помочь статическому анализатору подтвердить правильность вашего кода, если он не может справиться с ним самостоятельно. - person Anon.; 15.12.2010
comment
На самом деле статическая проверка только выиграла бы от Contract.Assume. Этот метод ведет себя как Contract.Assert во время выполнения, но говорит программе статической проверки не пытаться статистически доказать это. - person koenmetsu; 15.12.2010
comment
Contract.Assert — это просто документация для других программистов :) - person porges; 16.12.2010

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

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

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

person Jonathan Wood    schedule 15.12.2010
comment
На самом деле, 2-й и 3-й методы используют статический анализ, чтобы попытаться доказать их истинность во время компиляции. - person Anon.; 15.12.2010
comment
@Anon: Но статический анализатор не сможет поймать все нулевые исключения во время компиляции ... что произойдет, если одно из них проскользнет мимо? Вызывает ли он исключение, сбой, ничего не делает или что? - person mpen; 15.12.2010
comment
@Ralph: Если статический анализатор не может что-то доказать в любом случае (он не может показать, что контракт не работает в некоторых случаях, но он также не может доказать, что он всегда правильный), он покажет предупреждение компиляции (а не ошибку ) и замените его утверждением. - person Anon.; 15.12.2010