Вопрос по exception-handling, language-agnostic, exception – Зачем перебрасывать исключения?

34

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

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

    // or
    // throw;

    // or
    // throw ex;
}

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

Ваш Ответ

13   ответов
-3

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

1

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

Мой вопрос посвящен теме "Почему"? мы перебрасываем исключения и их использование в стратегии обработки исключений приложений.

0

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

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

38

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

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

Правильно ли я понял - нет необходимости обрабатывать повторно брошенное исключение?
Я имею в виду следующее: если у вас есть фрагмент кода, который не может обработать исключение (т. Е. Вы хотите, чтобы ваш вызывающий обработал его вместо этого), у вас есть два варианта: 1. не поймать его; 2. поймать его, войти где-нибудь, а затем сбросить. При повторном отбрасывании звонящему кажется, что его вообще не поймали.
1

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

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

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

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

10

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

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

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

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

& # X2022; Найдите хорошее имя, которое объясняет, почему возникло исключение, и убедитесь, что имя заканчивается словом & # x201C; Exception & # x201D ;.

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

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

& # X2022; Убедитесь, что вы реализовали конструктор десериализации.

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

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

& # X2022; Если вы добавляете какие-либо пользовательские свойства, переопределите свойство Message, чтобы добавить свои свойства к стандартному сообщению об исключении.

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

0

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

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

2

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

Keeping the set of thrown exception types fixed, as part of the API, so that the callers only have to worry about the fixed set of exceptions. In Java, you are practically forced to do that, because of the checked exceptions mechanism.

Adding some context information to the exception. For example, instead of letting the bare "record not found" pass through from the DB, you might want to catch it and add "... while processing order no XXX, looking for product YYY".

Doing some cleanup - closing files, rolling back transactions, freeing some handles.

1

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

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

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

8

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

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

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

0

Если вы посмотрите на исключения, как наan alternative way to get a method resultзатем повторное генерирование исключения похоже на перенос вашего результата в какой-либо другой объект.

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

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

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

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

Example

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

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

24

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

throw new CustomException(ex);

а также

throw;

Второй сохранит информацию стека.

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

Например:

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);
    } 
}
& Quot; Их & Quot ;? Неужели люди не проверяют свои сообщения об ошибках? Это как, например, Error материал! :-П
Ваше первое утверждение вводит в заблуждение. бросить новое CustomException (ex) не приведет к потере информации о стеке, оно будет сохранено в InnerException объекта CustomException. Это когда вы бросаете экс; что стек потерян.
Он потерян, как и в, трассировка стека, которую вы увидите в исходном исключении, начнется в вашем собственном улове. Да, вы все еще можете проверить InnerException на предмет реального начала исключения, но вы должны сами собрать кусочки. Бросок сохранит стек и полный «путь». останется.
Я извиняюсь: D Вы правы ... (Мой английский подвел меня: D)
@davy - предполагается, что реализация ToString включает также ToString без исключения, так что обычно она не теряется. Большинство исключений .Net Framework следуют этой практике (хотя я только что нашел одно - ServiceModel.FaultException - это не так). Единственная проблема в том, что вы просто должны знать, что регистрировать .ToString. Это лучшая практика, но я редко вижу ее обнародованной.
0

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

Похожие вопросы