Когда я должен использовать Debug.Assert ()? - PullRequest
212 голосов
/ 24 сентября 2008

Я работаю профессиональным инженером-программистом около года, закончив со степенью бакалавра. Некоторое время я знал об утверждениях в C ++ и C, но понятия не имел, что они существовали в C # и .NET вообще до недавнего времени.

Наш производственный код не содержит никаких утверждений, и мой вопрос заключается в следующем ...

Должен ли я начать использовать Asserts в нашем производственном коде? И если да, то когда его использование является наиболее подходящим? Будет ли больше смысла делать

Debug.Assert(val != null);

или

if ( val == null )
    throw new exception();

Ответы [ 20 ]

221 голосов
/ 24 сентября 2008

In Отладка приложений Microsoft .NET 2.0 У Джона Роббинса большой раздел с утверждениями. Его основные моменты:

  1. Утверждай свободно. Вы никогда не можете иметь слишком много утверждений.
  2. Утверждения не заменяют исключения. Исключения охватывают то, что требует ваш код; утверждения охватывают вещи, которые он принимает.
  3. Хорошо написанное утверждение может рассказать вам не только о том, что произошло и где (как исключение), но и почему.
  4. Сообщение об исключении часто может быть загадочным, требуя от вас работы в обратном направлении по коду для воссоздания контекста, вызвавшего ошибку. Утверждение может сохранить состояние программы в момент возникновения ошибки.
  5. Утверждения дублируются как документация, сообщая другим разработчикам, от каких предположений зависит ваш код.
  6. Диалоговое окно, появляющееся при сбое подтверждения, позволяет подключить к процессу отладчик, чтобы вы могли обходить стек, как если бы вы установили точку останова.

PS: Если вам понравился Code Complete, я рекомендую дополнить его этой книгой. Я купил его, чтобы узнать, как использовать WinDBG и файлы дампа, но первая половина содержит советы, которые помогут избежать ошибок.

84 голосов
/ 24 сентября 2008

Поместите Debug.Assert() везде в коде, где вы хотите, чтобы проверки работоспособности обеспечивали инварианты. Когда вы компилируете сборку Release (т.е. без DEBUG константы компилятора), вызовы Debug.Assert() будут удалены, поэтому они не повлияют на производительность.

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

50 голосов
/ 24 сентября 2008

С Код завершен

8 Защитное программирование

8,2 Утверждения

Утверждение - это код, который используется во время разработки - обычно это рутина или макрос - это позволяет программе проверять себя во время работы. Когда утверждение верно, это означает, что все работает, как ожидалось. Если значение равно false, это означает, что обнаружена непредвиденная ошибка в код. Например, если система предполагает, что информация о клиенте файл никогда не будет иметь более 50 000 записей, программа может содержать утверждение, что количество записей меньше или равно до 50000. Пока количество записей меньше или равно 50000, утверждение будет молчать. Если он встречает больше, чем 50000 записей, однако, он будет громко "утверждать", что есть ошибка в программе.

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

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

(…)

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

45 голосов
/ 17 февраля 2011

FWIW ... Я считаю, что мои публичные методы, как правило, используют шаблон if () { throw; }, чтобы гарантировать, что метод вызывается правильно. Мои частные методы имеют тенденцию использовать Debug.Assert().

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

Кроме того, мои личные методы могут по-прежнему генерировать исключения, если что-то не работает во время выполнения (ошибка сети, ошибка доступа к данным, неверные данные, полученные из сторонней службы и т. Д.). Мои утверждения только для того, чтобы убедиться, что я не нарушил свои внутренние предположения о состоянии объекта.

40 голосов
/ 24 сентября 2008

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

32 голосов
/ 24 сентября 2008

На вашем месте я бы сделал:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

или чтобы избежать повторной проверки состояния

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}
23 голосов
/ 24 сентября 2008

Если вы хотите, чтобы утверждения были в вашем производственном коде (т. Е. Выпускать сборки), вы можете использовать Trace.Assert вместо Debug.Assert.

Это, конечно, увеличивает накладные расходы на ваш производственный исполняемый файл.

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

Вы можете переопределить это поведение, удалив DefaultTraceListener: посмотрите документацию по Trace.Listeners в MSDN.

В итоге

  • Используйте Debug.Assert свободно, чтобы помочь выявить ошибки в сборках Debug.

  • Если вы используете Trace.Assert в режиме пользовательского интерфейса, вы, вероятно, захотите удалить DefaultTraceListener, чтобы избежать смущения пользователей.

  • Если условие, которое вы тестируете, является тем, что ваше приложение не может обработать, вам, вероятно, лучше выбросить исключение, чтобы выполнение не продолжалось. Помните, что пользователь может игнорировать утверждение.

21 голосов
/ 24 сентября 2008

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

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

10 голосов
/ 24 сентября 2008

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

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

Для меня это предполагает использование Debug.Asserts, вы откладываете проблему кому-то еще, решайте проблему самостоятельно. Если что-то должно быть так, а это не так, бросьте.

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

10 голосов
/ 16 октября 2014

Короче

Asserts используются для охранников и для проверки проектирования по условиям контракта, а именно:

  • Asserts должно быть только для отладочных и непроизводственных сборок. Утверждения обычно игнорируются компилятором в сборках Release.
  • Asserts может проверять наличие ошибок / непредвиденных ситуаций, которые находятся под контролем вашей системы
  • Asserts НЕ является механизмом для проверки первой строки пользовательского ввода или бизнес-правил
  • Asserts следует не использовать для обнаружения непредвиденных условий окружающей среды (которые находятся вне контроля кода), например Недостаточно памяти, сбой сети, сбой базы данных и т. д. Хотя эти ситуации встречаются редко, их следует ожидать (и код вашего приложения не может исправить такие проблемы, как сбой оборудования или истощение ресурсов). Как правило, генерируются исключения - ваше приложение может либо предпринять корректирующие действия (например, повторить попытку базы данных или выполнить сетевую операцию, попытаться освободить кэшированную память), либо изящно прервать работу, если исключение не может быть обработано.
  • Неудачное утверждение должно быть фатальным для вашей системы - т. Е. В отличие от исключения, не пытайтесь отлавливать или обрабатывать неудачные Asserts - ваш код работает на неожиданной территории. Следы стека и аварийные дампы могут использоваться для определения того, что пошло не так.

Утверждения имеют огромное преимущество:

  • Чтобы помочь в поиске пропущенной проверки пользовательских входных данных или вышестоящих ошибок в коде более высокого уровня.
  • Утверждения в базе кода четко передают читателю предположения, сделанные в коде
  • Утверждение будет проверено во время выполнения в Debug сборках.
  • После того, как код будет полностью протестирован, перестройка кода в виде Release устранит накладные расходы производительности при проверке предположения (но с тем преимуществом, что более поздняя сборка Debug всегда будет возвращать проверки при необходимости).

... Подробнее

Debug.Assert выражает условие, которое было принято о состоянии оставшейся частью блока кода в управлении программой. Это может включать в себя состояние предоставленных параметров, состояние членов экземпляра класса или то, что возврат от вызова метода находится в его контрактном / разработанном диапазоне. Как правило, утверждения должны приводить к аварийному завершению потока / процесса / программы со всей необходимой информацией (отслеживание стека, аварийный дамп и т. Д.), Поскольку они указывают на наличие ошибки или неучтенного условия, для которого не было разработано (то есть не пытаться поймать или обрабатывать ошибки подтверждения), с одним возможным исключением того, что само утверждение может нанести больше ущерба, чем ошибка (например, авиадиспетчеры не хотели бы YSOD, когда самолет идет на подводной лодке, хотя это спорный вопрос, следует ли развертывать отладочную сборку на производство ...)

Когда вы должны использовать Asserts? - В любой точке системы, или API библиотеки, или службы, где входные данные для функции или состояния класса считаются действительными (например, когда проверка уже была произведена при вводе данных пользователем на уровне представления системы, бизнес и Классы уровня данных обычно предполагают, что проверки нуля, проверки диапазона, проверки длины строки и т. д. на входе уже выполнены) - Общие проверки Assert включают случаи, когда неверное допущение может привести к разыменованию нулевого объекта, делителю нуля, числовому или числовому арифметическому переполнению и общему внешнему / не предназначенному для поведения (например, если для моделирования использовалось 32-битное целое человеческому возрасту было бы благоразумно Assert, что возраст на самом деле составляет от 0 до 125 или около того - значения от -100 до 10 ^ 10 не были рассчитаны).

.Кодовые контракты
В .Net Stack Кодовые контракты могут использоваться в дополнение или с использованием Debug.Assert. Контракты на кодирование могут дополнительно формализовать проверку состояния и могут помочь в обнаружении нарушений допущений во время компиляции (или вскоре после этого, если они запускаются в качестве фоновой проверки в IDE).

Доступны проверки по контракту (DBC):

  • Contract.Requires - Предварительные условия по контракту
  • Contract.Ensures - Контрактные условия
  • Invariant - Выражает предположение о состоянии объекта во всех точках его срока службы.
  • Contract.Assumes - устанавливает статическую проверку, когда выполняется вызов неконтрактных методов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...