Когда бросить исключение?


435

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

Однако мне сказали, что я не должен создавать исключения для этих условий. В моем UML эти ARE являются исключениями для основного потока, так почему же это не должно быть исключением?

Любое руководство или лучшие практики для создания исключений?


59
Пожалуйста, откройте, это очень разумный и правильный вопрос. Любой вопрос требует определенного количества мнений, но в этом случае я подозреваю, что это вопрос «наилучшей практики».
Тим Лонг

19
+1 для повторного открытия. Как и многие другие интересные темы, «это зависит», и это очень полезно для анализа компромиссов при принятии решений. Тот факт, что люди путают мнения с фактами в ответах, не отрицает этого. Просеять грязь - это упражнение, которое следует оставить читателю.
Арон

12
Я также согласен с тем, что этот вопрос следует вновь открыть, поскольку он связан с передовой практикой. Кстати, лучшие практики - это всегда мнения, которые могут помочь другим.
Аджай Шарма

6
Microsoft говорит: «Не возвращайте коды ошибок. Исключения являются основным средством сообщения об ошибках в средах». и «... Если участник не может успешно делать то, для чего он предназначен, это следует рассматривать как сбой при выполнении, и следует выдавать исключение». msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
Это может быть вполне разумное исключение, это зависит только от того, какие методы их выбрасывают. Вызванный метод IsCredentialsValid(username,password)не должен выдавать исключение, если имя пользователя или пароль недействительны, но должен возвращать false. Но, скажем, метод, который считывает данные из базы данных, может на законных основаниях выдать такое исключение, если аутентификация не удалась. Вкратце: вы должны вызвать исключение, если метод не может выполнить задачу, которую он должен делать.
JacquesB

Ответы:


629

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

Пример 1: скажем, у меня есть функция, которая должна проверять произвольный класс и возвращать true, если этот класс наследует от List <>. Эта функция задает вопрос: «Является ли этот объект потомком List?» Эта функция никогда не должна генерировать исключение, потому что в ее работе нет серых областей - каждый отдельный класс наследует или не наследует от List <>, поэтому ответ всегда «да» или «нет».

Пример 2: скажем, у меня есть другая функция, которая проверяет List <> и возвращает true, если его длина больше 50, и false, если длина меньше. Эта функция задает вопрос: «Есть ли в этом списке более 50 элементов?» Но этот вопрос делает предположение - он предполагает, что объект, который ему дан, является списком. Если я передаю ему значение NULL, то это предположение неверно. В том случае, если функция возвращает либо верно или неверно, то это нарушение свои собственные правила. Функция не может возвращать ничего и утверждает , что он ответил на вопрос правильно. Так что он не возвращается - он вызывает исключение.

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

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


15
Точно! Исключение выдается тогда и только тогда, когда предусловия функций (предположения об аргументах) нарушаются !
Лайтман

11
В лингвистике это иногда называют провалом предположения . Классический пример связан с Бертраном Расселом: «Является ли король Франции лысым», нельзя ответить ни да, ни нет (соответственно, «Король Франции лыс» не является ни верным, ни ложным), потому что он содержит ложную предпосылку а именно что есть король франции. Ошибка пресуппозиции часто видна с определенными описаниями, и это часто встречается при программировании. Например, «заголовок списка» имеет ошибку допущения, когда список пуст, и тогда уместно вызвать исключение.
Мохан

Это, пожалуй, лучшее объяснение!
Гаурав

Спасибо. Так. Многое. «Король Франции лысый». Я слышал это раньше, когда исследовал джунгли Мейнонга .... :) Спасибо. @Mohan
ErlichBachman

285

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


3
Хм нет Исключения можно использовать в качестве механизмов управления потоком, если не требуется максимальная производительность, что справедливо для большинства веб-приложений. В Python для исключения итераторов используется исключение «StopIteration», и оно работает очень хорошо. Стоимость ничто по сравнению с IO и т. Д.
Seun Osewa

9
+1 отличный ответ. Я так разочарован разработчиками, работающими над API, что мне приходится потреблять и выбрасывать исключения для каждой мелочи. ОЧЕНЬ мало случаев действительно требуют исключений. Если вы определили 25 различных типов исключений, взгляните на свой дизайн еще раз, возможно, вы делаете это неправильно.
7

1
должно ли быть исключение, когда пользователь пытается выполнить незаконное действие, которое ему не разрешено, манипулируя кодом веб-страницы, например, для удаления чужих постов здесь, в StackOverflow?
Раджат Гупта

30
Исключения составляют механизмы управления потоком. Вы можете бросить их. Вы можете поймать их. Вы переместили управление на другой кусок кода. Это поток управления. Единственная причина, по которой у языков есть исключения, заключается в том, что вы можете написать простой код, не спрашивая: «Это что-то не получилось?» после всего, что ты делаешь. Например, Haskell не имеет исключений, потому что монады и do-нотация могут автоматизировать проверку ошибок для вас.
Джесси

2
Исключением являются не только механизмы управления потоком. Они предоставляют клиенту (метода) полезную информацию об исключительных результатах, о которых они должны знать и обрабатывать. То есть, при правильном использовании исключения делают API более надежными
idelvall

67

Мои маленькие рекомендации находятся под сильным влиянием великой книги "Код завершен":

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

35

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

РЕДАКТИРОВАТЬ: я не люблю использовать исключения, потому что вы не можете сказать, если метод выдает исключение, просто посмотрев на вызов. Вот почему исключения следует использовать только в том случае, если вы не можете справиться с ситуацией приличным образом (подумайте «недостаточно памяти» или «компьютер горит»).


«Мне не нравится использовать исключения, потому что вы не можете определить, вызывает ли метод исключение, просто посмотрев на вызов». Вот почему существуют проверенные исключения для языков, которые их поддерживают.
Newtopian

1
Проверенные исключения имеют свой собственный набор проблем. Я бы все же предпочел использовать исключения из «исключительных обстоятельств», а не для вещей, которые являются частью обычного рабочего процесса.
EricSchaefer

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

1
должно ли быть исключение, когда пользователь пытается выполнить незаконное действие, которое ему не разрешено, манипулируя кодом веб-страницы, например, для удаления чужих постов здесь, в StackOverflow?
Раджат Гупта

1
Похоже, вы говорите об обработке ошибок (наша память, компьютер в огне) против обработки исключений кода (отсутствующая запись, неверный тип ввода и т. Д.). Я думаю, что между ними есть четкая разница.
Чак Берджесс

28

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


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

2
но зачем даже бросать их, если вы можете справиться с этим напрямую. если пароль неправильный или что-то не так, я просто позволяю if вернуть false и выдать ошибку
My1

« отсутствует файл на диске » Большинство языковых платформ, например .NET Framework, предоставляют API-интерфейсы для проверки существования файлов. Почему бы не использовать их до прямого доступа к файлу!
user1451111

23

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

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


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

7
Существующий файл не означает, что пользователю разрешено писать в файл, например. Проверка на каждую возможную проблему действительно утомительна и подвержена ошибкам. + вы дублируете код (СУХОЙ).
Бьорн Реппен

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

7
«Усложнение вашего кода путем распространения возвращаемых значений через n уровней вверх по стеку - пустая трата энергии и приведет к грязному коду». Для меня это очень веская причина использовать исключения. Хороший код обычно состоит из небольших функций. Вы не хотите, чтобы этот код ошибки передавался снова и снова из одной маленькой функции в другую.
Белучин

Я думаю, что путаница проистекает из предположения, что, возможно, предсказуемым результатом loginметода -типа будет то, что пароль может быть неправильным, он может фактически использоваться для определения этого и не хотел бы иметь исключение в этом случае; тогда как в file openсценарии типа, который имеет определенный желаемый результат - если система не может доставить результат из-за неправильных входных параметров или какого-либо внешнего фактора, это вполне логичное использование исключения.
The Mayer

17

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

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


14

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


Вы увидите множество советов, в том числе в ответах на этот вопрос, что вы должны бросать исключения только в «исключительных» обстоятельствах. Это кажется поверхностно разумным, но это ошибочный совет, потому что он заменяет один вопрос («когда я должен выбросить исключение») другим субъективным вопросом («что является исключительным»). Вместо этого следуйте совету Херба Саттера (для C ++, доступного в статье доктора Доббса « Когда и как использовать исключения» , а также в его книге с Андреем Александреску, « Стандарты кодирования C ++» ): выведите исключение тогда и только тогда, когда

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

Почему это лучше? Разве это не заменяет вопрос несколькими вопросами о предварительных условиях, постусловиях и инвариантах? Это лучше по нескольким связанным причинам.

  • Предварительные условия, постусловия и инварианты являются конструктивными характеристиками нашей программы (ее внутреннего API), а решение throw- деталью реализации. Это заставляет нас иметь в виду, что мы должны рассматривать проект и его реализацию отдельно, и наша задача при реализации метода заключается в создании чего-то, что удовлетворяет проектным ограничениям.
  • Это заставляет нас думать с точки зрения предусловий, постусловий и инвариантов, которые являются единственными допущениями, которые должны делать вызывающие нашего метода, и выражаются точно, обеспечивая слабую связь между компонентами нашей программы.
  • Это слабое связывание позволяет нам при необходимости реорганизовать реализацию.
  • Постусловия и инварианты тестируемы; это приводит к коду, который может быть легко протестирован модулем, потому что постусловия являются предикатами, которые наш код модульного теста может проверить (утвердить).
  • Мышление в терминах постусловий естественным образом создает дизайн, который имеет успех как постусловие , что является естественным стилем использования исключений. Нормальный («счастливый») путь выполнения вашей программы выложен линейно, а весь код обработки ошибок перемещен в catchпункты.

10

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

Причины использования исключений:

  • Поток кода для общего случая яснее
  • Может возвращать информацию о сложной ошибке как объект (хотя это также может быть достигнуто с помощью параметра «out» ошибки, переданного по ссылке)
  • Языки, как правило, предоставляют некоторые средства для управления аккуратной очисткой в ​​случае исключения (try / finally в Java, использование в C #, RAII в C ++)
  • Если исключение не выдается, выполнение может иногда выполняться быстрее, чем проверка кодов возврата.
  • В Java проверенные исключения должны быть объявлены или перехвачены (хотя это может быть причиной против)

Причины не использовать исключения:

  • Иногда излишне, если обработка ошибок проста
  • Если исключения не задокументированы или не объявлены, они могут быть отключены с помощью вызывающего кода, что может быть хуже, чем если бы вызывающий код просто игнорировал код возврата (выход из приложения против сбоя без вывода сообщений - что еще хуже, может зависеть от сценария)
  • В C ++ код, использующий исключения, должен быть безопасным для исключений (даже если вы не выбрасываете и не перехватываете их, а вызываете функцию выброса косвенно)
  • В C ++ трудно сказать, когда функция может выдать, поэтому вы должны быть параноиком по поводу безопасности исключений, если вы используете их
  • Создание и отлов исключений, как правило, значительно дороже по сравнению с проверкой флага возврата.

В целом, я был бы более склонен использовать исключения в Java, чем в C ++ или C #, потому что я придерживаюсь мнения, что исключение, объявленное или нет, является фундаментальной частью формального интерфейса функции, поскольку изменение гарантии исключения может сломать вызывающий код. Самым большим преимуществом их использования в Java IMO является то, что вы знаете, что вызывающая сторона ДОЛЖНА обработать исключение, и это повышает вероятность правильного поведения.

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

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


5

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

Как правило, вы должны попытаться найти баланс между количеством исключений и гранулярностью исключений. Если ваш метод генерирует более 4-5 различных исключений, вы, вероятно, можете объединить некоторые из них в более «общие» исключения (например, в вашем случае «AuthenticationFailedException») и использовать сообщение об исключении, чтобы подробно описать, что пошло не так. Если ваш код не обрабатывает каждый из них по-разному, вам не нужно создавать много классов исключений. И если это произойдет, пусть вы просто вернете перечисление с ошибкой, которая произошла. Так немного чище.


5

Если это код, выполняющийся внутри цикла, который, вероятно, будет вызывать исключение снова и снова, то создание исключений не очень хорошая вещь, потому что они довольно медленные для больших N. Но нет ничего плохого в создании пользовательских исключений, если производительность не проблема. Просто убедитесь, что у вас есть базовое исключение, которое они все наследуют, называемое BaseException или что-то подобное. BaseException наследует System.Exception, но все ваши исключения наследуют BaseException. Вы даже можете иметь дерево типов исключений для группировки похожих типов, но это может быть или не быть чрезмерным.

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


Мне очень понравился твой комментарий об исключениях внутри цикла, и я решил попробовать сам. Я написал пример программы, которая запускает время цикла int.MaxValueи генерирует в нем исключение «деление на ноль». Версия IF / ELSE, в которой я проверял, является ли дивиденд не нулевым, перед операцией деления , завершенной за 6082 мс и 15407722 тиков, тогда как версия TRY / CATCH, в которой я генерировал исключение и перехватывал исключение , завершилась в 28174385 мс и 71371326155 галочки: колоссальные в 4632 раза больше, чем версия if / else.
user1451111

3

Я согласен с Джаплоком, если вы не уверены в исходе операции. Вызовы API, доступ к файловым системам, вызовы базы данных и т. Д. Каждый раз, когда вы пересекаете «границы» ваших языков программирования.

Я хотел бы добавить, не стесняйтесь бросить стандартное исключение. Если вы не собираетесь делать что-то «другое» (игнорировать, отправлять по электронной почте, регистрировать, показывать, что твиттер изображает кита и т. Д.), То не беспокойтесь о пользовательских исключениях.


3

Практическое правило создания исключений довольно простое. Вы делаете это, когда ваш код перешел в НЕИЗВЕСТНОЕ НЕДЕЙСТВИТЕЛЬНОЕ состояние. если данные скомпрометированы или вы не можете свернуть обработку, которая произошла до этой точки, вы должны прекратить ее. действительно, что еще вы можете сделать? ваша логика обработки в конечном итоге потерпит неудачу в другом месте. если вы можете как-то восстановиться, делайте это и не бросайте исключения.

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


2

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

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

Они являются «исключениями» для основного потока вашего UML, но являются более «ветвями» в обработке.

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


« Если вы попытались получить доступ к файлу или базе данных passwd и не смогли, это было бы исключительным случаем и потребовало бы исключения». Большинство языковых платформ, например .NET Framework, также предоставляют API для проверки существования файла. Почему бы не использовать их до прямого доступа к файлу!
user1451111

2

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

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


2

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


2

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


2

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

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

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

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

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

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

Поэтому в примере входа в систему может быть целесообразно создать что-то вроде AuthenticationException и различать конкретные ситуации по значениям перечисления, таким как UsernameNotExisting , PasswordMismatch и т. Д. Тогда у вас не будет огромной иерархии исключений, и вы сможете поддерживать блоки catch на поддерживаемом уровне. , Вы также можете легко использовать некоторый общий механизм обработки исключений, так как вы классифицировали исключения и хорошо знаете, что распространять до пользователя и как.

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

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


2

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


1

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

В статье ниже утверждается, что исключение - исключительные условия и ошибки.

Неправильное имя пользователя - это не обязательно ошибка программы, а ошибка пользователя ...

Вот хорошая отправная точка для исключений в .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

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

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


1

У меня есть три типа условий, которые я ловлю.

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

  2. AppException. Обычно это исключение, которое вы обнаруживаете и добавляете в свой код. Другими словами, это те, которые вы ожидаете (файл не существует). Зарегистрируйте его, установите сообщение и вернитесь на страницу с общей ошибкой. На этой странице обычно есть немного информации о том, что произошло.

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

Надеюсь это поможет


1

Безопасность связана с вашим примером: вы не должны сообщать злоумышленнику, что имя пользователя существует, но пароль неправильный. Это дополнительная информация, которой вам не нужно делиться. Просто скажите «имя пользователя или пароль неверны».


1

Ответ прост: всякий раз, когда операция невозможна (либо из-за приложения, либо из-за нарушения бизнес-логики). Если метод вызывается и невозможно сделать то, для чего был написан метод, выдается исключение. Хорошим примером является то, что конструкторы всегда выдают ArgumentExceptions, если экземпляр не может быть создан с использованием предоставленных параметров. Другой пример - InvalidOperationException, который генерируется, когда операция не может быть выполнена из-за состояния другого члена или членов класса.

В вашем случае, если вызывается такой метод, как Login (имя пользователя, пароль), если имя пользователя недопустимо, действительно корректно выбрасывать исключение UserNameNotValidException или PasswordNotCorrectException, если пароль неверен. Пользователь не может войти в систему с помощью предоставленных параметров (т.е. это невозможно, потому что это нарушит аутентификацию), поэтому выведите исключение. Хотя, возможно, ваши два исключения унаследованы от ArgumentException.

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

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

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

Вот пример того, как избежать использования Исключений в сценарии, как только что описано, с помощью шаблона Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

На мой взгляд, фундаментальный вопрос должен заключаться в том, следует ли ожидать, что вызывающая сторона захочет продолжить нормальный поток программ, если возникает условие. Если вы не знаете, либо у вас есть отдельные методы doSomething и trySomething, где первый возвращает ошибку, а второй нет, либо есть подпрограмма, которая принимает параметр, указывающий, следует ли генерировать исключение в случае сбоя). Рассмотрим класс для отправки команд в удаленную систему и отчета об ответах. Определенные команды (например, перезапуск) заставят удаленную систему отправить ответ, но затем не будут отвечать на запросы в течение определенного периода времени. Таким образом, полезно иметь возможность отправлять команду «ping» и выяснять, отвечает ли удаленная система в течение разумного промежутка времени без необходимости генерировать исключение, если это не так. t (вызывающий может ожидать, что первые несколько попыток "ping" потерпят неудачу, но одна из них в конечном итоге сработает). С другой стороны, если есть последовательность команд, например:

  exchange_command ("открыть временный файл");
  exchange_command ("записать данные временного файла {что угодно}");
  exchange_command ("записать данные временного файла {что угодно}");
  exchange_command ("записать данные временного файла {что угодно}");
  exchange_command ("записать данные временного файла {что угодно}");
  exchange_command ("закрыть временный файл");
  exchange_command («скопировать временный файл в реальный файл»);

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

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


1

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

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

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

Итак, как правило:

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


0

Вы можете использовать несколько общих исключений для этих условий. Например, ArgumentException предназначен для использования, когда что-то не так с параметрами в методе (за исключением ArgumentNullException). Обычно вам не нужны исключения, такие как LessThanZeroException, NotPrimeNumberException и т. Д. Подумайте о пользователе вашего метода. Число условий, которые она хочет обработать специально, равно количеству типов исключений, которые должен выдавать ваш метод. Таким образом, вы можете определить, насколько подробными будут ваши исключения.

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


0

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

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

0

Существует два основных класса исключений:

1) Системное исключение (например, потеря соединения с базой данных) или 2) Пользовательское исключение. (например, подтверждение ввода пользователя, «пароль неверный»)

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

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

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

  1. какой уровень кода вы хотите запустить после появления кандидата на исключение, то есть сколько слоев стека вызовов должно быть разобрано. Как правило, вы хотите обработать исключение как можно ближе к месту его возникновения. Для проверки имени пользователя и пароля вы обычно обрабатываете сбои в одном и том же блоке кода, а не позволяете появиться исключению. Так что исключение, вероятно, не подходит. (OTOH, после трех неудачных попыток входа в систему поток управления может сместиться в другое место, и здесь может быть уместно исключение.)

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.