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

StackOverflow https://stackoverflow.com/questions/21938

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

Очевидно, это один из тех вопросов, на которые единственный реальный ответ — «это зависит».

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

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

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

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

или это в Python:

try:
    something()
except:
    pass

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

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

Другие советы

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

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

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

Да!(кроме «верхней части» вашего приложения)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Что касается моего класса 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();
    }

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top