Подтвердить что ты не робот

Что означает "Исключить только исключения, которые вы можете обрабатывать"?

Мне поручено написать документ стратегии и руководства по обработке исключений для проекта .NET/С#, над которым я работаю. У меня есть жесткое решение. Там есть много информации о том, как/когда бросать, ловить, обматывать исключения, но я ищу описание того, что должно происходить внутри блока catch, за исключением обертывания и исключения исключений.

try
{
   DoSomethingNotNice();
}
catch (ExceptionICanHandle ex)
{
   //Looking for examples of what people are doing in catch blocks
   //other than throw or wrapping the exception, and throwing.
}

Заранее спасибо

4b9b3361

Ответ 1

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

В принципе, правило существует для предотвращения анти-шаблонов вроде:

try
{
   ...
}
catch(Exception ex)
{
   throw;
}

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

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

EDIT: Чтобы ответить на вопрос в сообщении, а не только на тему, вы можете написать правило следующим образом: "Не кодируйте оператор try-catch, который ничего не делает, или только перескакивает исключенное пойманное. операторы должны выполнять какое-то действие с добавленной стоимостью, относящееся к выброшенному исключению".

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

  • Сервер не отвечает - вы можете попробовать еще раз; возможно, вызовите метод соединения рекурсивно в catch, с помощью "счетчика повторов", чтобы разбить бесконечный цикл в противном случае.
  • Аутентификация с ошибкой пользователя - показать дружественное (или не очень дружелюбное, но сжатое и понятное) сообщение в диалоговом окне.
  • Пользователь не авторизован для подключения к указанному БД - зависит от вашей настройки безопасности; в большинстве офисов, что-то, о чем вы должны отправить по электронной почте администратору базы данных, потому что это означает, что он создал логин, но забыл назначить соответствующие права.
  • Сеть недоступна: вы можете предупредить пользователя об ошибке в диалоговом окне входа в систему или в новом диалоговом окне, повторить попытку пару раз и т.д.
  • Деление на ноль - WTF? Что может вызвать Div по Zero во время входа? Вы не ожидаете этого исключения, вы не знаете, что пошло не так в этом случае и, следовательно, не может продолжать запуск кода, поэтому не поймите его.
  • Если что-то пойдет не так, вы можете записать сообщение в файл или общий ресурс для целей аудита/безопасности. Это должно происходить на более низких уровнях, если вы хотите продолжить выполнение, или более высокие уровни, если после этого вы будете изящно закрыты.

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

Основные правила для исключения исключений:

  • Если вы не ожидаете исключения, не поймайте его.
  • Если вы не можете или не хотите продолжать выполнение кода после получения исключения, знаете ли вы, что это может произойти или нет, не поймите его.
  • Если вы ожидаете возникновения исключения и знаете, как продолжать выполнение кода, когда это произойдет (по крайней мере на некоторое время), затем поймайте и выполните любые специальные действия, необходимые для этого.
  • НИКОГДА не задерживайте исключения (пустой блок catch); что заставляет приложения терпеть неудачу тихо еще более непредсказуемыми способами.
  • НИКОГДА не оставляйте catch-and-rethrow (блок catch только с ретровом) в производственном коде. Иногда они могут быть полезны при отладке, поскольку они позволяют вам идентифицировать определенные сегменты кода, которые терпят неудачу, но в производственном коде они просто являются ударом скорости, чтобы выкинуть или фактически справиться с исключением.

Ответ 2

Я думаю, что основная идея, лежащая в основе этого общего совета, заключается в том, чтобы избежать таких сценариев:

try
{
    SomeImportantResource = GetSomeImportantResource();
    SomeOtherImportantResource = GetSomeOtherImportantResource();
}
catch (Exception ex)
{
    SomeGlobalErrorHandlingMechanism(ex);
}

Я работал с разработчиками, которые, столкнувшись с ошибкой, просто обернули код нарушения в блок try/catch и скажут: "Я исправил ошибку". Проблема в сценариях, подобных приведенному выше, заключается в том, что, просто перехватывая исключение и не устраняя проблему, вызвавшую его, вы можете подорвать надежность своей программы. Выше, что сделало catch, мы не знаем, правильно ли были инициализированы SomeImportantResource и SomeOtherImportantResource. Представляется вероятным, что в программе может быть код, требующий инициализации этих файлов, и в этом случае мы только что внедрили ошибку "исправление" ошибки.

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

Или, лучше, чем это: не поймайте исключение и сделайте некоторую слабую попытку (или не пытайтесь) "обработать" ее; выяснить, что вызвало это и решить эту проблему. Очевидно, что это не всегда возможно, но это возможно гораздо чаще, чем должно быть.

Ответ 3

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

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

Это пример, в котором у вас есть определенное поведение программы, которое вы хотите, и выполните его, как вы обрабатываете исключение. Как правило, вы должны попытаться найти способ достижения своей цели, не используя обработку исключений, например, в приведенном выше примере, вы всегда можете проверить, доступен ли файл, прежде чем пытаться работать с ним. Таким образом, вы можете просто кодировать его как "if/else" вместо "try/catch". Однако, если вы это сделали, по-прежнему существует всегда вероятность того, что кто-то может потерять доступ к файлу в середине операции, так что независимо от того, был ли вы предварительно зарегистрирован, вы все еще может получить исключение, которое вы можете обработать изящно. Таким образом, вы, вероятно, реорганизовали бы свой блок else в функцию, которая вызывается из else и catch, так что вы можете изящно отказаться от локального хранилища в любом случае.

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

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

Ответ 4

Если вы можете выполнить какое-либо действие, когда вы поймаете какое-либо исключение, например, выполнение блока кода, который будет выполнять функцию, пытающуюся выполнить оператор try, но делает это в другом, но, возможно, менее эффективном или просто информировать пользователя о том, что их действие не может быть выполнено), тогда вы должны поймать его и сделать это. Если вы просто регистрируете исключение, чтобы отслеживать проблему позже, тогда вы должны перестроить исключение throw; (NOT throw ex;), если есть еще один блок кода, который может обрабатывать этот тип исключения.

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

Ответ 5

Некоторые примеры:

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

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

Ответ 6

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

Ответ 7

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

try
{
   DoSomethingNotNice();
}
catch (ExceptionIMightBeAbleToHandle ex)
{
   if(iCanHandle(ex))
       thenHandle(ex);
   else
       throw;
}

Обратите внимание, что использование throw само по себе должно сохранять информацию о трассировке стека.

Типичными вещами, которые вы можете обрабатывать грациозно, будет FileNotFoundException.

Ответ 8

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

Как только это было сделано, вы можете использовать throw;, чтобы вырезать исключение до следующего уровня

В качестве альтернативы вы можете обернуть свое текущее исключение в новом исключении, более релевантном текущему методу

catch(LowLevelException ex){
     throw new HighLevelException("argh bad things happened!",ex);

}

Ответ 9

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

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

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