Неужели так плохо ловить общее исключение?

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

Ответ 1

Очевидно, это один из тех вопросов, где единственный реальный ответ - "это зависит".

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

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

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

try { 
    something(); 
} catch (Exception ex) {}

или это в Python:

try:
    something()
except:
    pass

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

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

Ответ 2

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

Мое основное правило - поймать все исключения, которые вы ожидаете, и что-то еще является ошибкой.

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

Ответ 3

Да! (за исключением "верхнего" вашего приложения)

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

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

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

Ответ 4

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

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

Ответ 5

Было много философских дискуссий (больше подобных аргументов) об этой проблеме. Лично я считаю, что самое худшее, что вы можете сделать, - это проглатывать исключения. Следующее худшее позволяет исключить пузырь до поверхности, где пользователь получает скверный экран, полный технических mumbo-jumbo.

Ответ 6

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

Вы поймаете как IOException, так и NullPointerException с помощью общего исключения, но способ, которым вы программируете, может быть иным.

Ответ 7

Я думаю, что точка двоякая.

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

Во-вторых, рекомендации FxCop фокусируются на коде библиотеки /Framework - не все их правила предназначены для использования на веб-сайтах EXE или ASP.Net. Поэтому иметь глобальный обработчик исключений, который будет записывать все исключения и выходить из приложения хорошо, это хорошо.

Ответ 8

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

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

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

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

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

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

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

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

В этих случаях вы ВСЕГДА хотите перехватить Exception (возможно, даже Throwable иногда), чтобы поймать программные/непредвиденные ошибки, зарегистрировать их и продолжить.

Ответ 12

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

Ответ 13

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

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

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

До:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

После:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

Ответ 14

Непопулярное мнение: не совсем.

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

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

Имейте в виду, что некоторые исключения должны всплывать вверх почти в каждом случае, например, Python KeyboardInterrupt и SystemExit. К счастью для Python, они хранятся в отдельной ветки иерархии исключений, поэтому вы можете позволить им всплыть, перехватывая Exception. Хорошо продуманная иерархия исключений делает этот тип вещей действительно простым.

Основное время для отлова общих исключений будет вызывать серьезные проблемы, когда имеешь дело с ресурсами, которые необходимо очистить (возможно, в предложении finally), так как универсальный обработчик может легко пропустить подобные вещи. К счастью, это на самом деле не проблема для языков с defer, таких конструкций, как Python with или RAII в C++ и Rust.

Ответ 15

Осматривая общее исключение, я чувствую, что держу палку динамита внутри горящего здания и выталкивая взрыватель. Это помогает в течение короткого времени, но динамит будет в любом случае взорваться через какое-то время.

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