Подтвердить что ты не робот

Зачем перебрасывать исключения?

Я видел следующий код много раз:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

Не могли бы вы объяснить цель повторного выброса исключения? Является ли он примером/лучшей практикой в ​​обработке исключений? (Я где-то читал, что он называется шаблоном Caller Inform?)

4b9b3361

Ответ 1

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

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

Ответ 2

На самом деле существует разница между

throw new CustomException(ex);

и

throw;

Вторая будет сохранять информацию о стеке.

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

Например:

try
{

}
catch (SqlException ex)
{
    switch  (ex.Number) {
        case 17:
        case 4060:
        case 18456:
           throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
        case 547:
            throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
        default:
            throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
    } 
}

Ответ 3

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

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

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

Когда вы создаете настраиваемое исключение, вот полезный контрольный список:

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

• Убедитесь, что вы реализуете три стандартных конструктора исключений.

• Убедитесь, что вы отметили свое исключение атрибутом Serializable.

• Убедитесь, что вы выполняете конструктор десериализации.

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

• Если вы добавляете какие-либо настраиваемые свойства, убедитесь, что вы реализуете и переопределяете GetObjectData для сериализации ваших пользовательских свойств.

• Если вы добавляете какие-либо настраиваемые свойства, переопределите свойство Message, чтобы вы могли добавлять свои свойства к стандартным сообщениям о исключении.

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

Ответ 4

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

В основе приложения обычно ловят и повторно бросают, чтобы перевести исключение в нечто более значимое. Например, если вы пишете уровень доступа к данным и используете собственные коды ошибок с SQL Server, вы можете перевести SqlException в такие объекты, как ObjectNotFoundException. Это полезно, потому что (а) это облегчает обращение вызывающих абонентов к конкретным типам исключений и (б), поскольку оно предотвращает детали реализации этого уровня, такие как тот факт, что вы используете SQL Server для удержания утечки в другие слои, что позволяет вам легче менять вещи в будущем.

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

Ответ 5

Я могу думать о следующих причинах:

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

  • Добавление некоторой контекстной информации в исключение. Например, вместо того, чтобы позволить голым "запись не найдена" пройти через БД, вы можете ее поймать и добавить "... при обработке порядка XXX, ища продукт YYY".

  • Выполнение некоторых действий по очистке - закрытие файлов, откат транзакций, освобождение некоторых ручек.

Ответ 6

Как правило, "Do Something" либо включает в себя лучшее объяснение исключения (например, обертывание его в другом исключении), либо трассировку информации через определенный источник.

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

Это не означает, что этот метод используется по чисто веским причинам, он много раз используется, когда разработчик считает, что информация о трассировке может понадобиться в будущем, и в этом случае вы можете попробовать {} catch {throw;} style, что совсем не помогает.

Ответ 7

Я думаю, это зависит от того, что вы пытаетесь сделать с исключением.

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

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

Для этого будет множество других причин.

Ответ 9

До тех пор, пока я не начал использовать ExceptionBlock EntLib, я использовал их для регистрации ошибок перед их броском. Какой-то противный, когда вы думаете, что я мог бы справиться с ними в этот момент, но в то время было бы лучше, если бы они ненадолго провалились в UAT (после их регистрации), а не покрывали ошибку потока.

Ответ 10

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

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

Ответ 11

Как упоминал Рафал, иногда это делается для преобразования проверенного исключения в неконтролируемое исключение или к проверенному исключению, которое больше подходит для API. Здесь приведен пример:

http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html

Ответ 12

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

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

A -- calls --> B -- calls --> C

Если это не так, то на слое A, который вызывает слой B, будет полный набор исключений JDK для обработки.: -)

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

Пример

Слой A, сервлет: возвращает изображение и его метаинформацию
Layer B, библиотека JPEG: собирает доступные теги DCIM для анализа файла JPEG

Layer C, простая БД: запись строк класса из файла произвольного доступа. Некоторые байты сломаны, поэтому он выдает исключение, говорящее "не может читать строку UTF-8 для записи" bibliographicCitation ".

Итак, A не поймет значение 'bibliographicCitation'. Поэтому B должно перевести это исключение для A в TagsReadingException, который обертывает оригинал.

Ответ 13

ОСНОВНАЯ ПРИЧИНА исключений повторного выброса - это оставить незадействовавшим Call Stack, чтобы вы могли получить более полное представление о том, что происходит, и называет последовательность.