Где вы любите ловить исключения и почему?
Где вы любите ловить исключения и почему?
Мне интересно посмотреть, где люди находят полезным размещать свои блоки try/catch в надежде на появление каких-то общих шаблонов. Я опубликую свои два примера ответов на C++, но любой язык в порядке.
Одно место и причину за ответ, пожалуйста. Благодарю.
9 ответов
Не ловите ничего, с чем вы не готовы и не способны справиться.
Итак, имейте в виду обработку исключений верхнего уровня, чтобы бомбить приложение так, как вам нравится, на неожиданные исключения, а затем ловить только то, что вам нужно (как можно ближе к тому месту, где это может происходить), чтобы получить необходимую функциональность.
И вы должны сделать только одну из двух вещей: на самом деле сделать что-то, чтобы решить / обойти проблему или отбросить более описательное исключение, которое имеет пойманное исключение в качестве своего innerException
,
РЕДАКТИРОВАТЬ: Если вам нужно finally
блок (например, чтобы освободить что-то, что вы выделили в своем коде), и у вас нет ничего полезного для каких-либо исключений, которые могут появиться, применяется та же логика: просто не обрабатывайте их. Вместо этого используйте catch { throw; }
сбросить исключение на более высокий уровень, сохраняя всю информацию об исключении. (Или просто опустите блок catch, который, я думаю / надеюсь, делает то же самое?)
Я пытаюсь поймать ТОЛЬКО те исключения, с которыми я могу иметь дело.
Я ненавижу код, как это:
String s="12";
Integer i;
try {
i = Integer.parseInt(s);
} catch(ParseException pe) {
System.out.println("hihihihihihihi!!!);
}
Что я особенно ненавижу, так это то, что обычно все равно прерывается поток, потому что через три строки будет доступ к i, который будет предполагать, что i!= Null.
Затем вы будете читать свою трассировку стека, а также прокручивать, прокручивать и прокручивать журнал, пока не найдете первую значительную ошибку, из-за которой все остальное развалилось.
Я бы хотел, чтобы Java не заставляла меня ловить исключения, с которыми я не могу справиться. Но то, что я могу сделать, это:
catch(Exception e) {
throw new RuntimeException(e);
}
И я объявляю много "бросков" в моих определениях функций.
Я все еще мечтаю о дне, когда Eclipse автоматически откроет отладчик в правильной строке, когда он получит необработанное исключение. В тот день мой метод откроет правильную линию.
На других языках, таких как Smalltalk, я ловлю только те ошибки, которые могу обработать. И я с радостью бросаю необъяснимые исключения, когда ввод не соответствует моим ожиданиям.
Идея в том, что я не хочу, чтобы ошибка была зарегистрирована или задокументирована. Я хочу это исправить.
Я всегда ставлю улов в main() как улов последней инстанции:
int main( int argc, char** argv ) {
try {
Application application( argc, argv );
return application.result();
}
catch ( const std::exception& exception ) {
fprintf( stderr, "%s.\n", exception.what() );
}
}
В C# и Java я предпочитаю вообще не ловить исключения. Если я не могу избежать этого, я немедленно выбрасываю исключение RunTime.
Я всегда буду использовать самый жадный блок catch, который имеет самую большую необходимую область действия, но всегда с самым конкретным возможным типом исключения. Так как результатом блока catch является еще один бросок в 99,99% случаев, я пытаюсь сохранить полный метод в блоке try.
Мне нравится пытаться держать мой код обработки исключений отдельно от моего другого кода, поэтому я обычно создаю вспомогательный метод, который выполняет реальную логику, а внешний метод просто занимается обработкой исключений. Я всегда думал, что это дает коду чистый вид и делает его более читабельным.
public void stuff() throws MyException
{
try
{
tryStuff();
}
catch (SomeLibraryException e)
{
logger.log("Some message happened", e);
throw new MyException(e);
}
}
public void tryStuff() throws SomeLibraryException
{
// Do things in here that could throw exceptions
}
Мне нравится ловить обработчики в контроллере, которые обрабатывают события, запущенные из представления. Если исключения дают надежную гарантию безопасности, это полезный момент, потому что он достаточно высок для того, чтобы сообщать об ошибке, а обработчики обычно атомарны и связаны с чем-то, что пользователь только что сделал, так что, надеюсь, он сможет разобраться в чем дело.
void Controller::on_edit_entity( const Entity& entity ) {
try {
Command::ptr command = new EditEntityCommand( entity );
push_command( command );
}
catch ( const std::exception& exception ) {
fprintf( stderr, "%s.\n", exception.what() );
}
}
В Delphi Windows Application основной цикл обработки сообщений для вашего приложения (то есть в нижней части стека вызовов) обрабатывает ваши исключения, показывая окно сообщения. На мой взгляд, это лучшее место для обработки исключений.
Перехватывая исключения в ваших собственных методах с единственной целью показать пользователю окно сообщения, вы отказываете любому коду, вызывающему ваш метод, в том, что вы знаете, что исключение действительно произошло и что метод заражен ошибкой.
Я бы обработал исключение, только если:
- Мне нужно сделать некоторую очистку (например, откат БД), и в этом случае я бы повторно вызвал исключение после выполнения очистки.
- У меня есть еще немного информации, чтобы добавить к исключению.
- Мой метод может преуспеть в своей цели, несмотря на исключение.
(Прежде чем я начну: я парень из Java)
Что я рекомендую:
- Найдите ближайшую точку вверх по цепочке вызовов из источника исключения, где вы можете правильно обработать исключение - т.е. принять корректирующие меры, сигнализировать о сбое транзакции / действия и т. Д. (Сама регистрация не должна рассматриваться как обработка исключений) Все методы между обработчиком и метателем следует игнорировать исключение. Предпочитайте непроверенные исключения проверенным, чтобы исключения даже не фигурировали во всех этих промежуточных методах.
- Границы уровня и API должны указывать исключения, которые он может генерировать, используя проверенные исключения, поскольку обработка этих исключений является частью контракта для клиентского уровня / кода, который его использует.
- Напишите класс обработчика исключений с
handle(Exception e)
метод и выпустить это в команду изначально и убедитесь, что все используют его для обработки исключений. Основываясь на изменении сценариев обработки исключений, продолжайте добавлять перегруженные методы "handle" позже, чтобы модифицировать только обработчик. - Всегда помните о цепочке исключений при выполнении броска и броска. Это гарантирует, что полная причина исключения будет сообщено.
- Никогда не регистрируйте одну и ту же трассировку исключений более одного раза. Это делает его очень трудным для отладки с использованием файлов журнала.
- Методы верхнего уровня должны иметь предложение catch, которое перехватит любое исключение, которое может выдать система. Это предотвращает распространение нашей внутренней информации во внешний мир, если, не дай бог, в производственной среде что-то пойдет не так. Это скорее требование безопасности.
Два места:
- Во всех обработчиках событий
- В любом месте, где перехват может сделать что-то полезное, например, дополнить исключение информацией, которая будет полезна для отладки. Обычно новое исключение будет создано с этой информацией и выброшено. Обратите внимание, что InnerException (или не эквивалентный.NET) этого нового исключения должен быть установлен на исходное исключение, чтобы сохранить такие вещи, как трассировка стека.