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

<p>у меня есть исключения, созданные для каждого условия, которое мое приложение не ожидает. <code>usernamenotvalidexception</code>, <code>passwordnotcorrectexception</code> и т. д.</p><p>однако мне сказали, что я не должен создавать исключения для этих условий. в моем uml эти are являются исключениями для основного потока, так почему же это не должно быть исключением?</p><p>любое руководство или лучшие практики для создания исключений?</p>

<p>моя личная рекомендация такова: исключение выдается, когда фундаментальное предположение о текущем блоке кода оказывается ложным.</p><p>пример 1: скажем, у меня есть функция, которая должна проверять произвольный класс и возвращать true, если этот класс наследует от list <>. эта функция задает вопрос "является ли этот объект потомком list?" эта функция никогда не должна генерировать исключение, потому что в ее работе нет серых областей - каждый отдельный класс наследует или не наследует от list <>, поэтому ответ всегда "да" или "нет".</p><p>пример 2: скажем, у меня есть другая функция, которая проверяет list <> и возвращает true, если его длина больше 50, и false, если длина меньше. эта функция задает вопрос: "есть ли в этом списке более 50 элементов?" но этот вопрос делает предположение - он предполагает, что объект, который ему дан, является списком. если я передаю ему значение null, то это предположение неверно. в этом случае, если функция возвращает <i>или</i> правда <i>или же</i> ложь, то это нарушает свои собственные правила. функция не может вернуться <i>что-нибудь</i> и утверждают, что он правильно ответил на вопрос. так что он не возвращается - он вызывает исключение.</p><p>это сравнимо с <a href="http://en.wikipedia.org/wiki/fallacy\_of\_many\_questions">"загруженный вопрос"</a> логическая ошибка. каждая функция задает вопрос. если вводимые данные делают этот вопрос ошибочным, выведите исключение. эту линию труднее нарисовать с помощью функций, которые возвращают void, но суть в следующем: если предположения функции относительно ее входных данных нарушаются, она должна вызвать исключение, а не возвращаться нормально.</p><p>другая сторона этого уравнения: если вы обнаружите, что ваши функции часто генерируют исключения, вам, вероятно, нужно уточнить их допущения.</p>

<p>потому что это вещи, которые будут происходить нормально. исключения не являются механизмами управления потоком. пользователи часто неправильно вводят пароли, это не исключительный случай. исключения должны быть действительно редкой вещью, типа ситуаций <code>userhasdiedatkeyboard</code>.</p>

<p>мои маленькие рекомендации находятся под сильным влиянием великой книги "код завершен":</p><ul> <li>используйте исключения для уведомления о вещах, которые не следует игнорировать.</li> <li>не используйте исключения, если ошибка может быть обработана локально</li> <li>убедитесь, что исключения находятся на том же уровне абстракции, что и остальная часть вашей рутины.</li> <li>исключения должны быть зарезервированы для того, что <strong>действительно исключительное</strong>.</li> </ul>

<p>это не исключение, если имя пользователя неверно или пароль неверен. это то, что вы должны ожидать в нормальном режиме работы. исключения составляют вещи, которые не являются частью нормальной работы программы и встречаются довольно редко.</p><p>правка: я не люблю использовать исключения, потому что вы не можете сказать, если метод выдает исключение, просто посмотрев на вызов. вот почему исключения следует использовать только в том случае, если вы не можете справиться с ситуацией достойным образом (подумайте "не хватает памяти" или "компьютер горит").</p>

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

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

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

<p>я думаю, что вы должны генерировать исключение только тогда, когда вы ничего не можете сделать, чтобы выйти из своего текущего состояния. например, если вы выделяете память, а выделять ее нет. в тех случаях, когда вы упоминаете, вы можете четко восстановиться после этих состояний и соответственно вернуть код ошибки своему абоненту.</p><hr><p>вы увидите множество советов, в том числе в ответах на этот вопрос, что вы должны бросать исключения только в "исключительных" обстоятельствах. это кажется поверхностно разумным, но это ошибочный совет, потому что он заменяет один вопрос ("когда я должен выбросить исключение") другим субъективным вопросом ("что является исключительным"). вместо этого следуйте совету херба саттера (для c++, доступного в <a href="http://www.drdobbs.com/when-and-how-to-use-exceptions/184401836">статья доктора доббса <em> когда и как использовать исключения </em></a> , а также в своей книге с андреем александреску, <em> стандарты кодирования c++ </em>): бросить исключение, если и только если</p><ul> <li>предварительное условие не выполняется (что обычно делает невозможным одно из следующих действий) или</li> <li>альтернатива не будет соответствовать постусловию или</li> <li>альтернатива не сможет поддерживать инвариант.</li> </ul><p>почему это лучше? разве это не заменяет вопрос на <em> несколько </em> вопросов о предварительных условиях, постусловиях и инвариантах? это лучше по нескольким связанным причинам.</p><ul> <li>предварительные условия, постусловия и инварианты являются <em> конструктивными </em> характеристиками нашей программы (ее внутреннего api), в то время как решение <code>throw</code> является деталью реализации. это заставляет нас помнить о том, что мы должны рассматривать проект и его реализацию отдельно, и наша задача при реализации метода заключается в создании чего-то, что удовлетворяет проектным ограничениям.</li> <li>это заставляет нас думать с точки зрения предусловий, постусловий и инвариантов, которые являются <em> только </em> допущениями, которые должны делать вызывающие нашего метода, и выражаются точно, обеспечение слабой связи между компонентами нашей программы.</li> <li>это слабое связывание позволяет нам при необходимости реорганизовать реализацию.</li> <li>постусловия и инварианты тестируемы; это приводит к коду, который может быть легко протестирован модулем, потому что постусловия являются предикатами, которые наш код модульного теста может проверить (утвердить).</li> <li>мышление в терминах постусловий естественным образом создает дизайн, который имеет <em> успех как постусловие </em>, что является естественным стилем использования исключений. обычный ("счастливый") путь выполнения вашей программы выложен линейно, а весь код обработки ошибок перемещен в предложения <code>catch</code>.</li> </ul>

<p>я бы сказал, что нет жестких и быстрых правил, когда следует использовать исключения. однако есть веские причины использовать или не использовать их:</p><p>причины использования исключений:</p><ul> <li>поток кода для общего случая яснее</li> <li>может возвращать информацию о сложной ошибке как объект (хотя это также может быть достигнуто с помощью параметра "out" ошибки, переданного по ссылке)</li> <li>языки, как правило, предоставляют некоторые средства для управления аккуратной очисткой в случае исключения (try/finally в java, использование в c #, raii в c++)</li> <li>если исключение не выдается, выполнение может <em>иногда</em> быстрее проверки кодов возврата</li> <li>в java проверенные исключения должны быть объявлены или перехвачены (хотя это может быть причиной против)</li> </ul><p>причины не использовать исключения:</p><ul> <li>иногда излишне, если обработка ошибок проста</li> <li>если исключения не задокументированы или не объявлены, они могут быть отключены с помощью вызывающего кода, что может быть хуже, чем если бы вызывающий код просто игнорировал код возврата (выход из приложения против сбоя без вывода сообщений - что еще хуже, может зависеть от сценария)</li> <li>в c++ код, использующий исключения, должен быть безопасным для исключений (даже если вы не выбрасываете и не перехватываете их, а вызываете функцию выброса косвенно)</li> <li>в c++ трудно сказать, когда функция может выдать, поэтому вы должны быть параноиком по поводу безопасности исключений, если вы используете их</li> <li>создание и отлов исключений, как правило, значительно дороже по сравнению с проверкой флага возврата.</li> </ul><p>в общем, я был бы более склонен использовать исключения в java, чем в c++ или c #, потому что я придерживаюсь мнения, что исключение, объявленное или нет, является фундаментальной частью формального интерфейса функции, так как изменение гарантии исключения может привести к поломке вызывающего кода. самым большим преимуществом их использования в java imo является то, что вы знаете, что вызывающая программа должна обработать исключение, и это повышает вероятность правильного поведения.</p><p>из-за этого на любом языке я всегда получал бы все исключения в слое кода или api из общего класса, так что вызывающий код всегда может гарантировать перехват всех исключений. также я бы посчитал плохим выбрасывать классы исключений, специфичные для реализации, при написании api или библиотеки (то есть оборачивать исключения из нижних уровней, чтобы исключение, которое получает ваш вызывающий объект, было понятно в контексте вашего интерфейса).</p><p>обратите внимание, что java делает различие между общими и исключениями времени выполнения в том смысле, что последние не нужно объявлять. я бы использовал классы исключений времени выполнения только тогда, когда вы знаете, что ошибка является результатом ошибки в программе.</p>

<p>классы исключений похожи на "нормальные" классы. вы создаете новый класс, когда он "является" объектом другого типа, с разными полями и разными операциями.</p><p>как правило, вы должны попытаться найти баланс между количеством исключений и гранулярностью исключений. если ваш метод генерирует более 4-5 различных исключений, вы, вероятно, можете объединить некоторые из них в более "общие" исключения (например, в вашем случае "authenticationfailedexception") и использовать сообщение об исключении, чтобы подробно описать, что пошло не так. если ваш код не обрабатывает каждый из них по-разному, вам не нужно создавать много классов исключений. и если это произойдет, пусть вы просто вернете перечисление с ошибкой, которая произошла. так немного чище.</p>

<p>если это код, выполняющийся внутри цикла, который, вероятно, будет вызывать исключение снова и снова, то создание исключений не очень хорошая вещь, потому что они довольно медленные для больших n. но нет ничего плохого в создании пользовательских исключений, если производительность не проблема. просто убедитесь, что у вас есть базовое исключение, которое они все наследуют, называемое baseexception или что-то подобное. baseexception наследует system.exception, но все ваши исключения наследуют baseexception. вы даже можете иметь дерево типов исключений для группировки похожих типов, но это может быть или не быть чрезмерным.</p><p>итак, краткий ответ таков: если это не приводит к значительному снижению производительности (чего не должно быть, если вы не выбросите много исключений), тогда продолжайте.</p>

<p>я согласен с джаплоком, если вы не уверены в результате операции. вызовы api, доступ к файловым системам, вызовы базы данных и т.д. каждый раз, когда вы пересекаете "границы" ваших языков программирования.</p><p>я хотел бы добавить, не стесняйтесь бросить стандартное исключение. если вы не собираетесь делать что-то "другое" (игнорировать, отправлять по электронной почте, регистрировать, показывать, что твиттер изображает кита в twitter и т.д.), то не беспокойтесь о пользовательских исключениях.</p>

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

<p>я бы сказал, что, как правило, каждый фундаментализм ведет в ад.</p><p>вы, конечно, не захотите закончить потоком, управляемым исключениями, но вообще избегать исключений - тоже плохая идея. вы должны найти баланс между обоими подходами. что бы я не делал, это создавал тип исключения для каждой исключительной ситуации. это не продуктивно.</p><p>обычно я предпочитаю создавать два основных типа исключений, которые используются во всей системе: <em>logicalexception</em> и <em>technicalexception</em>. их можно дополнительно различать по подтипам, если это необходимо, но обычно это не является необходимым.</p><p>техническое исключение означает действительно неожиданное исключение, такое как отключение сервера базы данных, подключение к веб-службе, возникновение исключения ioexception и т.д.</p><p>с другой стороны, логические исключения используются для распространения менее серьезной ошибочной ситуации на верхние уровни (как правило, некоторый результат проверки).</p><p>обратите внимание, что даже логическое исключение не предназначено для регулярного использования для управления потоком программы, а скорее для того, чтобы выделить ситуацию, когда поток действительно должен закончиться. при использовании в java оба типа исключений <em>runtimeexception</em> подклассы, а обработка ошибок в значительной степени ориентирована на аспекты.</p><p>поэтому в примере входа в систему может быть целесообразно создать что-то вроде authenticationexception и различать конкретные ситуации по значениям перечисления, таким как <em>sernamenotexisting</em>, <em>passwordmismatch</em> и т.д. тогда вы не окажетесь в имеет огромную иерархию исключений и может поддерживать блоки catch на поддерживаемом уровне. вы также можете легко использовать некоторый общий механизм обработки исключений, так как вы классифицировали исключения и хорошо знаете, что распространять до пользователя и как.</p><p>мы обычно используем исключение logicalexception во время вызова веб-службы, когда ввод пользователя был неверным. исключение передается на детали soapfault, а затем снова отправляется на исключение на клиенте, что приводит к отображению ошибки проверки в одном поле ввода определенной веб-страницы, поскольку исключение имеет правильное сопоставление с этим полем.</p><p>это, конечно, не единственная ситуация: вам не нужно нажимать веб-сервис, чтобы вызвать исключение. вы можете сделать это в любой исключительной ситуации (например, в случае необходимости быстрого отказа) - все это на ваше усмотрение.</p>

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

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

<p>во-первых, если пользователи вашего api не заинтересованы в конкретных мелкозернистых ошибках, то наличие особых исключений для них не имеет никакого значения.</p><p>поскольку зачастую невозможно узнать, что может быть полезно для ваших пользователей, лучше использовать конкретные исключения, но обеспечить их наследование от общего класса (например, std :: exception или его производных в c++). это позволяет вашему клиенту ловить определенные исключения, если они захотят, или более общее исключение, если им все равно.</p>

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

<p>у меня есть три типа условий, которые я ловлю.</p><ol> <li> <p>плохой или отсутствующий ввод не должен быть исключением. используйте js на стороне клиента и регулярное выражение на стороне сервера для обнаружения, установки атрибутов и пересылки обратно на ту же страницу с сообщениями.</p> </li> <li> <p>appexception. обычно это исключение, которое вы обнаруживаете и добавляете в свой код. другими словами, это те, которые вы ожидаете (файл не существует). зарегистрируйте его, установите сообщение и вернитесь на страницу с общей ошибкой. на этой странице обычно есть немного информации о том, что произошло.</p> </li> <li> <p>неожиданное исключение. это те, о которых вы не знаете. зарегистрируйте это с деталями и отправьте их на страницу с общей ошибкой.</p> </li> </ol><p>надеюсь это поможет</p>

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

<p>простой ответ: всякий раз, когда операция невозможна (из-за любого приложения or, потому что это нарушит бизнес-логику). если метод вызывается и невозможно сделать то, для чего был написан метод, выдается исключение. хорошим примером является то, что конструкторы всегда выдают argumentexceptions, если экземпляр не может быть создан с использованием предоставленных параметров. другой пример - invalidoperationexception, который генерируется, когда операция не может быть выполнена из-за состояния другого члена или членов класса.</p><p>в вашем случае, если вызывается такой метод, как login (имя пользователя, пароль), если имя пользователя недопустимо, действительно корректно выбрасывать исключение usernamenotvalidexception или passwordnotcorrectexception, если пароль неверен. пользователь не может войти в систему, используя предоставленные параметры (т. е. это невозможно, потому что это нарушит аутентификацию), поэтому выведите исключение. хотя, возможно, ваши два исключения унаследованы от argumentexception.</p><p>сказав это, если вы не хотите генерировать исключение, потому что сбой входа в систему может быть очень распространенным, одна стратегия вместо этого создать метод, который возвращает типы, которые представляют различные сбои. вот пример:</p><pre><code>{ // 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)) { } } </code></pre><p>большинство разработчиков учат избегать исключений из-за накладных расходов, вызванных их выбросом. здорово быть внимательным к ресурсам, но обычно не в ущерб дизайну вашего приложения. это, вероятно, причина, по которой вам сказали не бросать ваши два исключения. использовать ли исключения или нет обычно сводится к тому, как часто будет происходить исключение. если это довольно распространенный или вполне ожидаемый результат, то это когда большинство разработчиков избегают исключений и вместо этого создают другой метод для указания сбоя из-за предполагаемого потребления ресурсов.</p><p>вот пример того, как избежать использования исключений в сценарии, как только что описано, с помощью шаблона try ():</p><pre><code>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; } } </code></pre>

<p>на мой взгляд, фундаментальный вопрос должен заключаться в том, следует ли ожидать, что вызывающая сторона захочет продолжить нормальный поток программ, если возникает условие. если вы не знаете, либо у вас есть отдельные методы dosomething и trysomething, где первый возвращает ошибку, а второй нет, либо есть подпрограмма, которая принимает параметр, указывающий, следует ли генерировать исключение в случае сбоя). рассмотрим класс для отправки команд в удаленную систему и отчета об ответах. определенные команды (например, перезапуск) заставят удаленную систему отправить ответ, но затем не будут отвечать на запросы в течение определенного периода времени. таким образом, полезно иметь возможность отправить команду "ping" и выяснить, отвечает ли удаленная система в течение разумного промежутка времени без необходимости генерировать исключение, если это не так (вызывающая сторона, вероятно, ожидает, что первые несколько " попытки пинга не увенчались успехом, но в конечном итоге это сработало). с другой стороны, если есть последовательность команд, например:</p><pre> exchange\_command ("открыть временный файл"); exchange\_command ("записать данные временного файла {что угодно}"); exchange\_command ("записать данные временного файла {что угодно}"); exchange\_command ("запись данных временного файла {что угодно}"); exchange\_command ("запись данных временного файла {что угодно}"); exchange\_command ("закрытие временного файла"); exchange\_command ("скопировать временный файл в реальный файл"); </pre><p>хотелось бы, чтобы сбой любой операции прерывал всю последовательность. хотя можно проверить каждую операцию, чтобы убедиться в ее успешности, более полезно, чтобы процедура exchange\_command () выдавала исключение в случае сбоя команды.</p><p>на самом деле, в приведенном выше сценарии может быть полезно иметь параметр для выбора ряда режимов обработки ошибок: никогда не генерировать исключения, генерировать исключения только для ошибок связи или генерировать исключения в любых случаях, когда команда не возвращает "успех" индикация.</p>

<p>основная причина, по которой следует избегать выбрасывания исключения, состоит в том, что при создании исключения возникает много накладных расходов.</p><p>в статье ниже утверждается, что исключение - исключительные условия и ошибки.</p><p>неправильное имя пользователя - это не обязательно ошибка программы, а ошибка пользователя ...</p><p>вот достойная отправная точка для исключений в .net: <a href="http://msdn.microsoft.com/en-us/library/ms229030(vs.80).aspx">http://msdn.microsoft.com/en-us/library/ms229030 (vs.80) .aspx</a> </p>

<p>безопасность связана с вашим примером: вы не должны сообщать злоумышленнику, что имя пользователя существует, но пароль неправильный. это дополнительная информация, которой вам не нужно делиться. просто скажите "имя пользователя или пароль неверны".</p>

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

<p>некоторые полезные вещи, которые следует учитывать при принятии решения о целесообразности исключения:</p><ol> <li> <p>какой уровень кода вы хотите запустить после появления кандидата на исключение, то есть сколько слоев стека вызовов должно быть разобрано. как правило, вы хотите обработать исключение как можно ближе к месту его возникновения. для проверки имени пользователя и пароля вы обычно обрабатываете сбои в одном и том же блоке кода, а не позволяете появиться исключению. так что исключение, вероятно, не подходит. (otoh, после трех неудачных попыток входа в систему поток управления может сместиться в другое место, и здесь может быть уместно исключение.)</p> </li> <li> <p>это событие вы хотели бы видеть в журнале ошибок? не каждое исключение записывается в журнал ошибок, но полезно спросить, будет ли полезна эта запись в журнале ошибок - то есть, вы попытаетесь что-то с этим сделать или будете мусором, который вы игнорируете.</p> </li> </ol>

<p>существует два основных класса исключений:</p><p>1) системное исключение (например, потеря соединения с базой данных) или 2) пользовательское исключение. (например, подтверждение ввода пользователя, "пароль неверный")</p><p>я нашел полезным создать свой собственный класс исключений пользователя, и когда я хочу выдать ошибку пользователя, я хочу, чтобы ее обрабатывали по-другому (т.е. ошибка ресурса отображалась для пользователя), тогда все, что мне нужно сделать в моем основном обработчике ошибок, это проверить тип объекта. :</p><pre><code> if typename(ex) = "userexception" then display(ex.message) else displayerror("an unexpected error has occured, contact your help desk") logerror(ex) end if </code></pre>

<p>"passwordnotcorrectexception" не является хорошим примером для использования исключений. ожидается, что пользователи неправильно вводят свои пароли, поэтому вряд ли это исключение. вы, вероятно, даже восстанавливаетесь после этого, показывая сообщение об ошибке nice, так что это просто проверка достоверности.</p><p>необработанные исключения в конечном итоге остановят выполнение - и это хорошо. если вы возвращаете ложные, нулевые коды или коды ошибок, вам придется самостоятельно решать состояние программы. если вы забудете где-нибудь проверить условия, ваша программа может продолжать работать с неверными данными, и вам может быть трудно понять, <em>что</em> произошло и <em>где</em>.</p><p>конечно, вы можете вызвать ту же проблему с пустыми операторами catch, но, по крайней мере, определить их проще и не требует от вас понимания логики.</p><p>итак, как правило:</p><p>используйте их там, где вы не хотите или просто не можете восстановиться после ошибки.</p>

<p>вы можете использовать несколько общих исключений для этих условий. например, argumentexception предназначен для использования, когда что-то не так с параметрами в методе (за исключением argumentnullexception). обычно вам не нужны исключения, такие как lessthanzeroexception, notprimenumberexception и т.д. подумайте о пользователе вашего метода. число условий, которые она хочет обработать специально, равно количеству типов исключений, которые должен выдавать ваш метод. таким образом, вы можете определить, насколько подробными будут ваши исключения.</p><p>кстати, всегда старайтесь предоставить пользователям ваших библиотек несколько способов избежать исключений. tryparse - хороший пример, он существует для того, чтобы вам не приходилось использовать int.parse и перехватывать исключение. в вашем случае вы можете захотеть предоставить несколько методов для проверки правильности имени пользователя или пароля, чтобы вашим пользователям (или вам) не приходилось много обрабатывать исключения. надеемся, что это приведет к более удобочитаемому коду и лучшей производительности.</p>

<p>в конечном счете, решение сводится к тому, будет ли более полезным иметь дело с ошибками на уровне приложения, такими как эта, с помощью обработки исключений или с помощью собственного собственного механизма, такого как возврат кодов состояния. я не думаю, что есть жесткое правило о том, что лучше, но я бы подумал:</p><ul> <li>кто звонит на ваш код? это какой-то общедоступный api или внутренняя библиотека?</li> <li>какой язык вы используете? если это, например, java, то генерирование (проверенного) исключения накладывает явное бремя на вашего вызывающего, чтобы каким-то образом обрабатывать это состояние ошибки, а не возвращать статус, который можно игнорировать. это может быть хорошо или плохо.</li> <li>как обрабатываются другие ошибки в том же приложении? вызывающие абоненты не хотят иметь дело с модулем, который обрабатывает ошибки уникальным образом, в отличие от чего-либо еще в системе.</li> <li>сколько вещей может пойти не так с рассматриваемой процедурой, и как они будут обрабатываться по-другому? рассмотрим разницу между серией блоков перехвата, которые обрабатывают различные ошибки, и переключением на код ошибки.</li> <li>у вас есть структурированная информация об ошибке, которую нужно вернуть? создание исключения дает вам лучшее место для размещения этой информации, чем просто возвращение статуса.</li> </ul>