Когда я должен использовать Debug.Утверждать ()?


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

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

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

Debug.Assert(val != null);

или

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

20 ответов:

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

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

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

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

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

с Код

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

8.2 утверждения

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

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

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

(...)

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

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

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

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

на твоем месте я бы сделал:

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

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

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}

Если вы хотите утверждает в вашем производственном коде (т. е. релиз строит), вы можете использовать трассировку.Утверждение вместо отладки.Утверждать.

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

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

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

в целом,

  • Использовать Для Отладки.Утверждайте либерально, чтобы помочь поймать ошибки в отладочных сборках.

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

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

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

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

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

Мне не нравится тот факт, что он делает отладочную сборку функционально отличной от сборки выпуска. Если debug assert терпит неудачу, но функциональность работает в выпуске, то как это имеет смысл? Это даже лучше, когда asserter давно покинул компанию, и никто не знает, что часть кода. Тогда вам придется убить часть своего времени изучение вопроса, чтобы увидеть, если это действительно проблема или нет. Если это проблема, то почему человек не бросает в первую очередь?

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

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

Короче

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

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

утверждения имеют огромную пользу:

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

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

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

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

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

проектирование по контракту (ДБН) доступна проверка включает в себя:

  • Contract.Requires - По Контракту Предпосылки
  • Contract.Ensures - Контракт Постусловий
  • Invariant - выражает предположение о состоянии объекта на всех этапах его жизни.
  • Contract.Assumes - умиротворяет статическая проверка при вызове Неконтрактных оформленных методов.

по словам Стандартные Компании Idesign вы должны

утверждать каждое предположение. В среднем каждая пятая строка является утверждением.

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

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

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

учитывая ваш пример check-for-null, если это находится во внутреннем API, я мог бы использовать утверждение. Если это в публичном API, я бы определенно использовал явную проверку и бросок.

все утверждения должны быть кодом, который может быть оптимизирован для:

Debug.Assert(true);

потому что это проверка того, что вы уже предположили, что это правда. Например:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

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

в первом случае, нет никаких проблем.

во втором случае есть проблема с вызывающим кодом-он не должен был вызывать GetFirstAndConsume С null, так что он получает исключение обратно.

в третьем случае, есть проблема с этим кодом, потому что он уже должен был быть проверен, что en != null прежде чем он был вызван, так что это не правда является ошибкой. Или другими словами, это должен быть код, который теоретически может быть оптимизирован для Debug.Assert(true), sicne en != null должно быть true!

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

1) что-то я не видел упомянутый здесь дополнительное концептуальное покрытие утверждает, что может обеспечить во время автоматизированного тестирования. Как простой пример:

когда какой-то вызывающий объект более высокого уровня изменяется автором, который считает, что они расширили область действия кода для обработки дополнительных сценариев, в идеале (!) они будут писать модульные тесты для покрытия это новое условие. Тогда может оказаться, что полностью интегрированный код работает нормально.

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

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

2) кроме того, некоторые тесты просты в написании, но дороговато и ненужно с учетом первоначальных предположений. Например:

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

3) далее, в некоторых случаях продукт может не иметь полезного диагностического взаимодействия для всех или части его операций при развертывании в режиме выпуска. Например:

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

4) и наконец, некоторые тесты не нужны только потому, что вызываемый воспринимается как чрезвычайно надежный. В большинстве случаев, более многоразовые код, тем больше усилий было приложено к тому, чтобы сделать его надежным. Поэтому обычно исключение для неожиданных параметров от вызывающих объектов, но утверждение для неожиданных результатов от вызываемых объектов. Например:

если ядро String.Find состояние операции он вернет a -1 если критерии поиска не найдены, вы можете безопасно выполнить одну операцию, а не три. Однако, если он действительно вернулся -2, у вас может не быть разумного курса действий. Было бы бесполезно замените более простой расчет на тот, который тестирует отдельно для A -1 значение и неразумно в большинстве сред выпуска засорять ваш код тестами, гарантируя, что основные библиотеки работают так, как ожидалось. В этом случае утверждения идеальны.

цитата взята из прагматичный программист: от подмастерья до мастера

Оставьте Утверждения Включенными

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

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

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

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

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

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

вы всегда должны использовать второй подход (исключений).

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

вы должны использовать Debug.Assert для проверки логических ошибок в ваших программах. Компилятор может только сообщить об ошибках синтаксиса. Поэтому вы должны определенно использовать операторы Assert для проверки логических ошибок. Например, тестирование программы, которая продает автомобили, которые только BMW, которые являются синими, должны получить скидку 15%. Компилятор мог бы рассказать вам ничего о том, если ваша программа является логически правильным в выполнении этого, но оператор assert может.

Я прочитал ответы здесь, и я подумал, что должен добавить важное различие. Существует два очень разных способа использования утверждений. Один из них является временным ярлыком разработчика для "этого не должно произойти, поэтому, если он даст мне знать, чтобы я мог решить, что делать", вроде условной точки останова, для случаев, когда ваша программа может продолжать. Другой, это как способ поставить предположения о допустимых состояниях программы в вашем коде.

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

но во втором случае утверждения являются частью кода. Они, ну, утверждают, что ваши предположения верны, а также документируют их. В таком случае, вы действительно хотите оставить их в коде. Если программа находится в недопустимом состоянии она не должна быть разрешена для продолжения. Если бы вы не могли позволить себе хит производительности, вы бы не использовали C#. С одной стороны, было бы полезно иметь возможность подключить отладчик, если это произойдет. С другой стороны, вы не хотите, чтобы трассировка стека появлялась у ваших пользователей и, возможно, что более важно, вы не хотите, чтобы они могли игнорировать ее. Кроме того, если он находится в службе, он всегда будет игнорироваться. Поэтому в производстве правильным поведением было бы выбросить исключение, а используйте обычную обработку исключений вашей программы, которая может показать пользователю хорошее сообщение и зарегистрировать детали.

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

using System.Diagnostics;

public class ExceptionTraceListener : DefaultTraceListener
{
    [DebuggerStepThrough]
    public override void Fail(string message, string detailMessage)
    {
        throw new AssertException(message);
    }
}

public class AssertException : Exception
{
    public AssertException(string message) : base(message) { }
}

и в рабочий конфигурационный файл:

<system.diagnostics>
  <trace>
    <listeners>
      <remove name="Default"/>
      <add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
    </listeners>
  </trace>
 </system.diagnostics>

Я не знаю, как это происходит в C# и .NET, но в C будет assert() работать только при компиляции с-DDEBUG - конечный пользователь никогда не увидит assert (), если он скомпилирован без. Это только для разработчиков. Я использую его очень часто, иногда легче отслеживать ошибки.

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

также нужно быть осторожным внутри asp.net, как утверждение может отображаться на консоли и заморозить запрос(ы).