Я прочитал множество статей (и пару других подобных вопросов, которые были опубликованы на StackOverflow) о том, как и когда использовать утверждения, и я хорошо их понял. Но все же я не понимаю, какая мотивация должна побуждать меня использовать Debug.Assert
вместо простого исключения. Я имею в виду, что в .NET ответ по умолчанию на неудачное утверждение - «остановить мир» и показать пользователю окно сообщения. Хотя такое поведение можно изменить, я считаю, что это очень раздражает и излишне, а вместо этого я мог бы просто выбросить подходящее исключение. Таким образом, я мог легко записать ошибку в журнал приложения непосредственно перед тем, как выбросить исключение, и, кроме того, мое приложение не обязательно зависает.
Итак, почему я должен использовать Debug.Assert
вместо простого исключения , если это вообще возможно? Размещение утверждения там, где его не должно быть, может вызвать все виды «нежелательного поведения», поэтому, с моей точки зрения, я действительно ничего не добьюсь, используя утверждение вместо того, чтобы генерировать исключение. Вы согласны со мной, или я что-то здесь упускаю?
Примечание. Я полностью понимаю, в чем разница «теоретически» (отладка и выпуск, шаблоны использования и т. Д.), Но, как я это вижу, мне было бы лучше выбросить исключение, чем выполнять утверждение. Поскольку, если в производственном выпуске обнаруживается ошибка, я все равно хотел бы, чтобы «утверждение» провалилось (в конце концов, «накладные расходы» смехотворно малы), поэтому мне лучше вместо этого выбросить исключение.
Изменить: как я это вижу, если утверждение не удалось, это означает, что приложение вошло в какое-то поврежденное, неожиданное состояние. Так зачем мне продолжать казнь? Не имеет значения, работает ли приложение на отладочной или выпускной версии. То же самое касается обоих
источник
Ответы:
Хотя я согласен с тем, что ваши рассуждения правдоподобны - то есть, если утверждение неожиданно нарушается, имеет смысл остановить выполнение, выбрасывая - я лично не буду использовать исключения вместо утверждений. Вот почему:
Как говорили другие, утверждения должны документировать ситуации, которые невозможны , таким образом, чтобы, если предположительно невозможная ситуация произойдет, разработчик будет проинформирован. Исключения, напротив, предоставляют механизм потока управления для исключительных, маловероятных или ошибочных ситуаций, но не невозможных. Для меня ключевое отличие заключается в следующем:
ВСЕГДА должна быть возможность создать тестовый пример, который выполняет данный оператор throw. Если создать такой тестовый пример невозможно, значит, в вашей программе есть путь к коду, который никогда не выполняется, и его следует удалить как мертвый код.
НИКОГДА не должно быть возможности создать тестовый пример, который вызывает срабатывание утверждения. Если утверждение срабатывает, либо код неверен, либо утверждение неверно; так или иначе, что-то нужно изменить в коде.
Вот почему я бы не стал заменять утверждение исключением. Если утверждение не может сработать, то его замена на исключение означает, что в вашей программе есть непроверяемый путь кода . Я не люблю непроверяемые пути кода.
источник
Утверждения используются для проверки понимания мира программистом. Утверждение должно потерпеть неудачу только в том случае, если программист сделал что-то не так. Например, никогда не используйте утверждение для проверки ввода пользователя.
Утверждает тест на условия, которые «не могут произойти». Исключения составляют условия, которые «не должно происходить, но происходят».
Утверждения полезны, потому что во время сборки (или даже во время выполнения) вы можете изменить их поведение. Например, часто в сборках релизов утверждения даже не проверяются, потому что они создают ненужные накладные расходы. Этого также следует опасаться: ваши тесты могут даже не выполняться.
Если вы используете исключения вместо утверждений, вы теряете некоторую ценность:
Код более подробный, поскольку для тестирования и выдачи исключения требуется как минимум две строки, а для утверждения - только одна.
Ваш тестовый код и код выброса будут всегда выполняться, в то время как утверждения могут быть скомпилированы.
Вы теряете связь с другими разработчиками, потому что утверждения имеют другое значение, чем код продукта, который проверяет и бросает. Если вы действительно тестируете программное утверждение, используйте assert.
Подробнее здесь: http://nedbatchelder.com/text/assert.html
источник
РЕДАКТИРОВАТЬ: В ответ на редактирование / примечание, которое вы сделали в своем сообщении: похоже, что использование исключений - это правильный выбор, а не утверждения для того типа вещей, которые вы пытаетесь выполнить. Я думаю, что ваш мысленный камень преткновения состоит в том, что вы рассматриваете исключения и утверждения для достижения той же цели, и поэтому вы пытаетесь выяснить, какое из них было бы «правильным» для использования. Хотя может быть некоторое совпадение в том, как могут использоваться утверждения и исключения, не путайте, что для них это разные решения одной и той же проблемы - это не так. Утверждения и исключения имеют свои собственные цели, сильные и слабые стороны.
Я собирался напечатать ответ своими словами, но это лучше отражает концепцию, чем я:
C # Station: утверждения
По сути, используйте исключения для вещей, которые необходимо отловить / обработать в производственном приложении, используйте утверждения для выполнения логических проверок, которые будут полезны для разработки, но отключены в производственной среде.
источник
Я думаю, что (надуманный) практический пример может помочь пролить свет на разницу:
(адаптировано из расширения MoreLinq Batch )
// '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... */ }
Итак, как сказал Эрик Липперт и другие, вы утверждаете только то, что вы ожидаете быть правильным, на тот случай, если вы (разработчик) случайно использовали его неправильно где-то еще, чтобы вы могли исправить свой код. Вы в основном генерируете исключения, когда не контролируете или не можете предвидеть, что входит, например, для пользовательского ввода , так что все, что передало ему неверные данные, могло реагировать соответствующим образом (например, пользователь).
источник
Еще один самородок из Code Complete :
Далее он добавляет несколько рекомендаций о том, что следует и не следует утверждать.
С другой стороны, исключения:
Если у вас нет этой книги, купите ее.
источник
Debug.Assert по умолчанию будет работать только в отладочных сборках, поэтому, если вы хотите отловить какое-либо плохое неожиданное поведение в ваших сборках выпуска, вам нужно будет использовать исключения или включить константу отладки в свойствах вашего проекта (что рассматривается в вообще не будет хорошей идеей).
источник
Используйте утверждения для вещей, которые ВОЗМОЖНЫ, но не должны происходить (если бы это было невозможно, зачем бы вы ставили утверждение?).
Разве это не похоже на случай использования
Exception
? Почему бы вам использовать утверждение вместоException
?Потому что должен быть код, который вызывается перед вашим утверждением, что остановит параметр утверждения как ложный.
Обычно перед вами нет кода,
Exception
который гарантирует, что он не будет брошен.Почему хорошо, что
Debug.Assert()
компилируется в prod? Если вы хотите узнать об этом во время отладки, разве вы не хотели бы знать об этом в продукте?Вы хотите этого только во время разработки, потому что, обнаружив
Debug.Assert(false)
ситуации, вы пишете код, чтобы гарантировать, чтоDebug.Assert(false)
это больше не повторится. После завершения разработки, если вы обнаружилиDebug.Assert(false)
ситуации и исправили их, ихDebug.Assert()
можно безопасно скомпилировать, поскольку теперь они являются избыточными.источник
Предположим, вы являетесь членом довольно большой команды и несколько человек работают над одной и той же общей базой кода, включая перекрытие классов. Вы можете создать метод, который вызывается несколькими другими методами, и, чтобы избежать конфликта блокировок, вы не добавляете к нему отдельную блокировку, а скорее «предполагаете», что он был ранее заблокирован вызывающим методом с определенной блокировкой. Например, Debug.Assert (RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); Другие разработчики могут пропустить комментарий, в котором говорится, что вызывающий метод должен использовать блокировку, но они не могут это игнорировать.
источник