Практика обработки исключений - PullRequest
9 голосов
/ 13 октября 2008

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

На данный момент мое понимание подсказывает мне, что для обертывания исключения потребуется создать исключение типа DriveNotFound (в IO), а затем обернуть его общим IOException.

Но с концепцией распространения исключения, это только то, что происходит, если у меня есть пустое предложение catch? Таким образом, в веб-приложении ASP.NET оно будет распространяться на global.asax. Или в случае недавно развернутого веб-приложения необработанное HTTPException дало желтый экран смерти и записало журнал в Windows Server (это веб-приложение, которое я переписываю). Таким образом, исключение происходит в методе, оно может быть обработано на уровне класса, отображено на странице, а затем переходит к global.asax или Windows Server.

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

Является ли цепочка обработки исключений просто предложениями try и catch (или catchs)? Я предполагаю из формулировки, да.

Наконец, почему и как бы я хотел, чтобы исключение распространялось по стеку вызовов?

Я прочитал руководство MS PandP по обработке исключений, но, думаю, примеры не привлекли меня достаточно, чтобы полностью понять все.

Этот вопрос от Enterprise Library - возможность обернуть / распространить исключение и т. Д. Я не уверен в распространении и различиях в замене / переносе исключения.

Также допустимо ли вставлять сложную логику обработки ошибок в блок catch (например, ifs / elses и тому подобное).

Спасибо

Ответы [ 5 ]

10 голосов
/ 14 октября 2008

Не менее 6 вопросов: -)

Но с концепцией распространения исключения, это только то, что происходит, если у меня есть пустое предложение catch?

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

Почему именно я хочу заключить исключение в более общее?

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

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

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

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

Извините, но я все еще не понимаю, как вы определяете эти два как разные.

Является ли цепочка обработки исключений просто предложениями try и catch (или catchs)?

Вот основы того, что происходит, когда выдается исключение:

  • CLR последовательно проходит по списку блоков Catch в локальном блоке Try ... End Try, ища локальный блок Catch с фильтром исключений, совпадающим с выданным исключением.

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

  • В качестве альтернативы, если сгенерированное исключение происходит из исключения, указанного локальным блоком Catch, происходят те же действия, что и описанные на втором шаге. Например, фильтр исключений, который перехватывает ArgumentException, также будет перехватывать исключения, полученные из ArgumentException, такие как ArgumentNullException, InvalidEnumArgumentException, DuplicateWaitObjectException и ArgumentOutOfRangeException.

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

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

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

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

  • Не следует размещать код очистки в блоке finally, который может вызвать исключение, если только этот код не находится в своем собственном блоке Try. Без этой дополнительной защиты CLR ведет себя так, как будто новое исключение было сгенерировано после окончания после блока finally, и ищет в стеке вызовов удаленный блок Catch, который хочет ответить на новое исключение. Исходное исключение будет потеряно, если только исходный блок Catch не сохранил его.

Наконец, почему и как бы я хотел, чтобы исключение распространялось по стеку вызовов?

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

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

Также допустимо ли вставлять сложную логику обработки ошибок в блок catch (например, ifs / elses и тому подобное).

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

5 голосов
/ 13 октября 2008

Уже есть довольно хороший вопрос по этому поводу с множеством хороших ответов и обсуждений. Смотрите здесь:

Рекомендации по обработке исключений в приложении Windows Forms?

5 голосов
/ 13 октября 2008

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

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

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

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

throw ex;

отличается от:

throw;

Первый отбрасывает старую трассировку стека и создает другую из точки, где выбрасывается исключение. Последний сохраняет исходную трассировку стека.

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

4 голосов
/ 13 октября 2008

Я обычно делаю это:

  • Сборки бизнес-логики (dll) не обрабатывают исключения, если они не полностью поняты
  • Исключения, которые ожидаются, но не обрабатываются, заключаются в один тип исключений и могут свернуть стек вызовов (т. Е. Все различные исключения, которые могут возникнуть во время взаимодействия с базой данных, помещаются в одно RepositoryException и выбрасываются) 1006 *
  • Неожиданные исключения могут распространяться (никогда не включайте catch(Exception ex) в dll)
  • Исключения обрабатываются только в последний возможный момент (т. Е. Контроллер для пользовательского интерфейса)

Обычно только в стеке вызовов вы точно знаете, что делаете (каков текущий бизнес-процесс) и как правильно обрабатывать исключения.

0 голосов
/ 23 октября 2008

Это лучший ресурс, который я нашел для реализации стратегии согласованной обработки исключений в .NET

http://msdn.microsoft.com/en-us/library/cc511522.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...