Каковы принципы, определяющие вашу политику обработки исключений?

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

Как вы решаете, когда использовать исключения? Есть ли у вас согласованная политика в отношении исключений?

Ответ 1

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

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

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

N.B. Не путайте гарантии исключений с спецификациями исключений.

Гарантии исключений:

Нет гарантии:

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

Основная гарантия:

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

Сильная гарантия: (ака Транзакционная гарантия)

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

Нет гарантии броска:

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

Ответ 2

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

Короче:

  • Fatal. Ужасные ошибки, указывающие на ваш процесс, полностью невосстанавливаются. Очистите все ресурсы, которые вы можете, но не поймайте их. Если вы пишете код, который способен обнаруживать такую ​​ситуацию, обязательно бросьте. Пример: исключение из памяти.

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

  • Vexing. Относительно простые ошибки, которые вы не используете, вы бросаете на вас. Вы должны поймать все это и справиться с ними, как правило, таким же образом, как вы бы справились с исключением из Boneheaded. Пожалуйста, не бросайте их снова обратно. Пример: исключение формата из С# метод Int32.Parse()

  • Exogenous. Сравнительно простые ошибки, которые очень похожи на Vexing (от кода других людей) или даже Boneheaded (из вашего кода), но должны быть выброшены, потому что реальность диктует, что код что бросать их на самом деле не имеет понятия, как восстановить, но, скорее всего, будет. Идем дальше и бросаем их, но когда ваш код получает их из другого места, поймайте их и разобраться с ними. Пример: Исключение файла не найдено.

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

Ответ 3

  • Никогда не бросайте исключения из деструкторов.

  • Поддерживать базовый уровень исключений, гарантирующих состояние объекта.

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

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

  • Не просто catch(...) и ничего не делать. Поймать исключения, о которых вы знаете, или конкретные исключения. По крайней мере, что случилось.

  • Когда в мире исключения используется RAII, потому что ничего не безопасно.

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

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

  • Знайте о флагах, которые могут вызывать такие библиотеки, как STL, для исключения исключений, вместо того, чтобы показывать неизвестное поведение (например, недопустимое переполнение итераторов/векторных индексов).

  • Захватывать ссылки вместо копий объекта исключения?

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

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

  • Не исключайте исключения из неэкспортированных библиотек dll export/C, потому что некоторые компиляторы оптимизируются, предполагая, что код C не генерирует исключения.

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

    main 
    {
         try {
        all code....
        }
        catch(...) {} 
    }
    

Ответ 4

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

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

Я разработчик .NET, и для catch и throw мой подход:

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

Ответ 5

Контекст данного ответа приведен в языке Java.

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

Однако мы не бросаем проверенные исключения, никогда. Мы подклассифицируем RuntimeException для наших собственных особых исключений, улавливая их, где это применимо напрямую, а что касается исключений, которые генерируются другими библиотеками, API JDK и т.д., Мы пытаемся/улавливаем внутренне и регистрируем исключение (если что-то произошло, что действительно должно 't есть, и у вас нет способа восстановить, как файл, не найденный исключение для пакетного задания) или мы завершаем исключение в RuntimeException, а затем бросаем его. С внешней стороны кода мы полагаемся на обработчик исключений, чтобы в конечном итоге поймать это RuntimeException, будь то JVM или веб-контейнер.

Причина этого заключается в том, что он избегает создания принудительных блоков try/catch везде, где у вас могут быть четыре экземпляра вызова метода, но только один может фактически обработать исключение. Кажется, это правило, а не исключение (не каламбур... ouch), поэтому, если этот четвертый может справиться с этим, он все равно сможет его поймать и изучить основную причину исключения, чтобы получить фактическое исключение, которое произошло ( не беспокоясь об обертке RuntimeException).

Ответ 6

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

Исключения доступа

  • Создание или подключение к любому соединению (удаленному, локальному).
    • Происходит в: Базах данных, удалении
    • Причины: Отсутствует, уже используется или недоступно, Недостаточно/Недействительные учетные данные
  • Открытие, чтение или запись на любой ресурс
    • Происходит в: File I/O, Database
    • Причины: заблокированные, недоступные, недостаточно/недопустимые учетные данные

Целостность данных

  • Может быть много случаев, когда целостность данных имеет значение
    • Что он ссылается, что он содержит...
    • Ищите ресурсы по методам или коду, для которых требуется набор критериев для очистки данных и в допустимом формате.
    • Пример: попытка разобрать строку со значением "bleh" на число.

Действительность данных

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

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

Ответ 7

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

Если вы используете С++, я рекомендую вам хотя бы попытаться прочитать, что Бьярне Страуструп (изобретатель С++) должен сказать о безопасности исключений. См. приложение E его книги "Язык программирования С++".

Он тратит 34 страницы, пытаясь объяснить, как безопасно работать с исключениями. Если вы понимаете его совет, то это должно быть все, что вам нужно знать.

Ответ 8

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

Я не уверен, что развитие, связанное с контрактами, связано с вопросом об улавливании исключений, но в целом вы должны ловить только те исключения, которые вы ожидаете и можете разумно восстановить. Например, большая часть кода не может осмысленно восстанавливаться из исключения Out Of Memory, поэтому нет смысла его ловить. С другой стороны, если вы пытаетесь открыть файл для записи, вы можете (и должны) обрабатывать случай, когда файл заблокирован только другим процессом или случай, когда файл был удален (даже если вы проверили его существо, прежде чем пытаться его открыть).

Как отметил другой комментатор, вы также должны избегать использования исключений для обработки ожидаемых условий, которых можно ожидать и избегать. Например, в платформе .NET int.TryParse предпочтительнее int.Parse с try/catch, особенно при использовании в цикле или таком.

Ответ 10

Как разработчик С++, моя собственная политика заключается не в том, чтобы исключать исключения из того, что я считаю публичным apis для моих классов/модулей (на самом деле это требование с COM). Тем не менее, я широко использую исключения в реализации частного класса. Например, работа с ATL:

HRESULT Foo()
{
    HRESULT hr = S_OK;
    try {
        // Avoid a whole lot of nested ifs and return code
        // checking - internal stuff just throws.
        DoStuff();
        DoMoreStuff(); // etc.
    } catch ( CAtlException& e ) {
        hr = e;
    }
    return hr;
}

void DoSomething()
{
    // If something goes wrong, AtlThrow( E_FAILED or E_WHATEVER ); 
}

Ответ 11

Моя политика обработки исключений можно найти по адресу:

http://henko.net/imperfection/exception-handling-policy-throwing-exception/.

(Надеюсь, что это не против правил продвижения веб-сайта, но здесь слишком много информации для вставки здесь.)

Ответ 12

Не исключение, вызванное языковой средой в соответствии со спецификацией. языка, если он действительно имеет понятие исключений? Я думаю о "делении на ноль" в Java или CONSTRAINT_ERROR в Ada против ничего вообще в C.

Как программист может "решить" использовать исключения после выбора языка программирования, который имеет исключения, определенные в его составе?

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

Edit2: Вы можете проверить бесплатную главу из книги Стивена Дьюхерста "С++ Gotchas", в частности Gotcha 64 и Gotcha 65. Хотя он сфокусирован на С++, уроки полезны на других языках.