Подтвердить что ты не робот

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

Сверху моей головы я могу думать о 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 просто тупой.

4b9b3361

Ответ 1

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

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

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


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

  • Контрактное выражение выражения преобразуется в утверждение контрактного переписывающего устройства, и статический анализатор пытается доказать выражение на основе имеющихся доказательств. Если это невозможно проверить, вы получите предупреждение о статическом анализе.
  • Выражение Contract.Assume игнорируется контрактным переписывающим устройством (насколько мне известно), но интерпретируется статическим анализатором как новое доказательство, которое оно может принимать во внимание при его статическом анализе. Contract.Assume используется для "заполнения пробелов" в статическом анализе, либо там, где ему не хватает сложностей, чтобы делать необходимые выводы или когда они взаимодействуют с кодом, который не был украшен Контрактами, так что вы можете предположить, например, что конкретный вызов функции возвращает ненулевой результат.
  • Contract.Requires - это условия, которые всегда должны быть истинными при вызове метода. Они могут быть ограничениями на параметры для метода (которые являются наиболее типичными), а также могут быть ограничениями на публично видимые состояния объекта (например, вы можете разрешить вызов метода только в том случае, если Initialized is True.) Эти типы ограничений нажимают на пользователей вашего класса либо проверять инициализацию при использовании объекта (и, предположительно, обрабатывают ошибку соответственно, если это не так), либо создают свои собственные ограничения и/или инварианты класса, чтобы уточнить, что Инициализация действительно произошла.

Ответ 2

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

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

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