Где вы любите ловить исключения и почему?

Где вы любите ловить исключения и почему?

Мне интересно посмотреть, где люди находят полезным размещать свои блоки 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)
Что я рекомендую:

  1. Найдите ближайшую точку вверх по цепочке вызовов из источника исключения, где вы можете правильно обработать исключение - т.е. принять корректирующие меры, сигнализировать о сбое транзакции / действия и т. Д. (Сама регистрация не должна рассматриваться как обработка исключений) Все методы между обработчиком и метателем следует игнорировать исключение. Предпочитайте непроверенные исключения проверенным, чтобы исключения даже не фигурировали во всех этих промежуточных методах.
  2. Границы уровня и API должны указывать исключения, которые он может генерировать, используя проверенные исключения, поскольку обработка этих исключений является частью контракта для клиентского уровня / кода, который его использует.
  3. Напишите класс обработчика исключений с handle(Exception e) метод и выпустить это в команду изначально и убедитесь, что все используют его для обработки исключений. Основываясь на изменении сценариев обработки исключений, продолжайте добавлять перегруженные методы "handle" позже, чтобы модифицировать только обработчик.
  4. Всегда помните о цепочке исключений при выполнении броска и броска. Это гарантирует, что полная причина исключения будет сообщено.
  5. Никогда не регистрируйте одну и ту же трассировку исключений более одного раза. Это делает его очень трудным для отладки с использованием файлов журнала.
  6. Методы верхнего уровня должны иметь предложение catch, которое перехватит любое исключение, которое может выдать система. Это предотвращает распространение нашей внутренней информации во внешний мир, если, не дай бог, в производственной среде что-то пойдет не так. Это скорее требование безопасности.

Два места:

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