Отлаживать.Утверждать против исключений


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

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

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


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

7 82

7 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* 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.

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

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

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

(взято из пакетное расширение 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... */
}

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

еще один самородок из Код:

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

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

Он идет дальше, чтобы добавить некоторые руководящие принципы в отношении того, что следует и чего не следует утверждать.

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

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

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

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

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

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

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

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

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

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