Debug.Assert vs Exception Throwing

Я прочитал много статей (и несколько других подобных вопросов, которые были опубликованы в StackOverflow) о том, как и когда использовать утверждения, и я их хорошо понял. Но, тем не менее, я не понимаю, какая мотивация должна заставить меня использовать Debug.Assert вместо того, чтобы бросать простое исключение. Я имею в виду, что в.NET ответ по умолчанию на неудавшееся утверждение - это "остановить мир" и отобразить окно сообщения пользователю. Хотя такое поведение может быть изменено, я нахожу его крайне раздражающим и избыточным, чтобы сделать это, хотя вместо этого я мог бы просто подобрать подходящее исключение. Таким образом, я мог бы легко записать ошибку в журнал приложения перед тем, как выбросить исключение, и, кроме того, мое приложение не обязательно замерзает.

Итак, почему я должен, если вообще, использовать Debug.Assert вместо простого исключения? Размещение утверждения, в котором он не должен быть, может вызвать все виды "нежелательного поведения", поэтому, на мой взгляд, я действительно ничего не получаю, используя утверждение вместо того, чтобы бросать исключение. Вы согласны со мной, или я что-то пропустил?

Примечание. Я полностью понимаю, в чем разница "в теории" (Debug vs Release, шаблоны использования и т.д.), Но, как я вижу, мне лучше избавиться от исключения вместо выполнения assert. Поскольку, если ошибка обнаружена в производственном выпуске, я все равно хочу, чтобы "утверждение" потерпело неудачу (в конце концов, "накладные расходы" смехотворно малы), поэтому мне лучше избавиться от исключения.


Изменить: способ, которым я это вижу, если утверждение не удалось, это означает, что приложение ввело какое-то поврежденное, неожиданное состояние. Итак, почему я хочу продолжить выполнение? Не имеет значения, работает ли приложение на отладочной или выпускной версии. То же самое касается обоих

Ответ 1

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

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

  • ВСЕГДА можно создать тестовый пример, который выполняет задание броска. Если невозможно создать такой тестовый пример, тогда у вас есть путь кода в вашей программе, который никогда не выполняется, и его следует удалить как мертвый код.

  • НИКОГДА не должно быть возможности создать тестовый пример, который вызывает предупреждение. Если возникает ошибка, либо код неверен, либо утверждение неверно; в любом случае что-то нужно изменить в коде.

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

Ответ 2

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

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

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

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

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

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

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

Подробнее здесь: http://nedbatchelder.com/text/assert.html

Ответ 3

EDIT: В ответ на редактирование/примечание, которое вы сделали в своем сообщении: Похоже, что использование исключений - это правильная вещь для использования над утверждениями для типа вещей, которые вы пытаетесь выполнить. Я думаю, что ментальный камень преткновения, который вы нажимаете, заключается в том, что вы рассматриваете исключения и утверждения для достижения той же цели, и поэтому вы пытаетесь выяснить, какой из них "правильный" использовать. Хотя может быть некоторое совпадение в том, как можно использовать утверждения и исключения, не путайте, что для них существуют разные решения одной и той же проблемы - они не являются. У утверждений и исключений есть свои цели, сильные и слабые стороны.

Я собирался набрать ответ своими словами, но это делает концепцию лучшей справедливостью, чем я:

Станция С#: Утверждения

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

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

* the condition should never be false if the code is correct,
* the condition is not so trivial so as to obviously be always true, and
* the condition is in some sense internal to a body of software.

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

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

Ответ 4

Я думаю, что (надуманный) практический пример может помочь осветить разницу:

(адаптировано из Расширение пакета MoreLinq)

// 'public facing' method
public int DoSomething(List<string> stuff, object doohickey, int limit) {

    // validate user input and report problems externally with exceptions

    if(stuff == null) throw new ArgumentNullException("stuff");
    if(doohickey == null) throw new ArgumentNullException("doohickey");
    if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");

    return DoSomethingImpl(stuff, doohickey, limit);
}

// 'developer only' method
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {

    // validate input that should only come from other programming methods
    // which we have control over (e.g. we already validated user input in
    // the calling method above), so anything using this method shouldn't
    // need to report problems externally, and compilation mode can remove
    // this "unnecessary" check from production

    Debug.Assert(stuff != null);
    Debug.Assert(doohickey != null);
    Debug.Assert(limit > 0);

    /* now do the actual work... */
}

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

Ответ 5

Другой самородок из Код завершен:

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

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

Далее он добавляет некоторые рекомендации о том, что следует и не следует утверждать.

С другой стороны, исключения:

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

Если у вас нет этой книги, вы должны ее купить.

Ответ 6

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

Ответ 7

Использование утверждений для вещей, которые возможны, но не должно произойти (если бы это было невозможно, то почему бы вы положили утверждение?).

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

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

Обычно перед вашим Exception нет кода, который гарантирует, что он не будет выброшен.

Почему хорошо, что Debug.Assert() скомпилирован в prod? Если вы хотите узнать об этом в отладке, не хотите ли вы узнать об этом в prod?

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

Ответ 8

Предположим, вы являетесь членом довольно большой команды, и есть несколько человек, работающих на одной общей базе кода, включая перекрытие классов. Вы можете создать метод, который вызывается несколькими другими методами, и чтобы избежать конфликта блокировок, вы не добавляете к нему отдельную блокировку, а скорее "предполагаете", что он был ранее заблокирован вызывающим методом с определенной блокировкой. Например, Debug.Assert(RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); Другие разработчики могут игнорировать комментарий, в котором говорится, что вызывающий метод должен использовать блокировку, но они не могут игнорировать это.