2008-09-22 3 views
33

Я видел следующий код много раз:Зачем перебрасывать исключения?

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

    // or 
    // throw; 

    // or 
    // throw ex; 
} 

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

ответ

38

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

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

+0

Правильно ли я понял - нет необходимости обрабатывать исключение повторного броска? – chester89 2008-11-26 11:03:51

+4

Что я имею в виду: если у вас есть кусок кода, который не может справиться с исключением (т. Е. Вы хотите, чтобы ваш вызывающий абонент обрабатывал его), у вас есть два варианта: 1. не поймайте его; 2. поймайте его, запишите его где-нибудь, а затем снимите. Перевернувшись, он появляется у вызывающего, как будто он никогда не был пойман. – 2008-11-27 05:04:22

0

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

0

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

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

1

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

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

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

1

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

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

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

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

-3

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

1

FYI, это связанный с ним вопрос о каждом типе повторного броска: Performance Considerations for throwing Exceptions

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

8

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

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

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

24

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

throw new CustomException(ex); 

и

throw; 

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

Но иногда вы хотите сделать исключение более «дружественным» к вашему домену приложения, вместо того чтобы позволить 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); 
    } 
} 
2

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

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

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

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

10

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

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

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

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

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

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

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

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

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

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

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

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

0

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

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

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

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

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

Пример

слой А, сервлет: извлекает изображение, и это метаинформацию
Layer B, библиотека JPEG: собирает доступные теги DCIM для разбора файла изображения JPEG
Layer C , простой DB: класс чтения строки записи из файла произвольного доступа. Некоторые байты сломаны, поэтому он выдает исключение, говорящее «не может читать строку UTF-8 для записи« bibliographicCitation ».

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