Вы когда-нибудь НЕ поймаете исключение или выбросите исключение, которое не будет поймано? - PullRequest
3 голосов
/ 20 марта 2009

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

Или, по крайней мере, НЕ ловить исключение?

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

Ответы [ 14 ]

15 голосов
/ 20 марта 2009

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

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

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

  • Используете ли вы исключения в качестве прокси-сервера для реальной регистрации? Почти никогда нет причин для этого; вместо этого рассмотрим реальный механизм регистрации. Поймай исключение и заставь регистратор выяснить, что произошло.

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

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

8 голосов
/ 20 марта 2009

Есть очень хорошее правило, с которым я столкнулся некоторое время назад:

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

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

Другое правило, которому нужно следовать:

Не поймайте исключение, если вы не знаете, что вы хотите с ним делать.

Очевидно, что вы должны включить код очистки в блок try ... finally, но вы никогда не должны просто перехватывать исключение только ради его перехвата. И вы никогда не должны молча глотать исключения. Хотя бывают случаи, когда вы можете захотеть перехватить все исключения (например, выполнив catch (Exception ex) в C #), они довольно редки и обычно имеют очень специфическую техническую причину. Например, если вы используете потоки в .NET 2.0 или более поздней версии, если из вашего потока выходит исключение, это приведет к выгрузке всего домена приложения. В этих случаях, однако, как минимум, вы должны записать детали исключения как ошибку и предоставить объяснение в комментариях.

5 голосов
/ 20 марта 2009

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

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

Итак, я бы посоветовал вам вместо этого спросить: «Когда я должен поймать исключение?»

4 голосов
/ 20 марта 2009

Конечно. Например, если вы пытаетесь загрузить несколько байтов в строку в Java:

try {
  String myString = new String(byteArray, "UTF-8");
} catch (UnsupportedEncodingException e) {
  // Platform doesn't support UTF-8?  What is this, 1991?
  throw new RuntimeExceptione(e);
}

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

3 голосов
/ 20 марта 2009

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

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

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

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

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

Таким образом, исключение похоже на договорное соглашение между вызывающим абонентом и поставщиком. Поставщик говорит: «Я либо сделаю работу, либо скажу, почему я не могу. То, что вы делаете, это ваше личное дело». И звонящий говорит: «Хорошо, если вы не можете выполнить эту работу, просто скажите мне, почему, и я решу, что делать в этом случае».

1 голос
/ 20 марта 2009

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

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

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

Кроме того, происходит нечто очень важное, когда вы перехватываете исключение: будут выполняться блоки finally для всего стека вызовов внутри блока try (и всего, что он вызывает). Что делают эти блоки в конце концов? Ну ничего. И если программа находится в неизвестном состоянии, я действительно имею в виду что-нибудь . Они могут стереть ценные данные клиентов с диска. Они могут бросить больше исключений. Они могут повредить данные в памяти, делая невозможным диагностирование ошибки.

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

Во-вторых, вы должны выбросить исключение, зная, что оно не будет поймано? Я думаю, что этот вопрос неправильно понимает природу методов многократного использования. Вся идея метода заключается в том, что он имеет «контракт», который следует: он принимает определенные параметры и возвращает определенное значение, а также выдает определенные исключения при определенных условиях. Это контракт - звонящий сам решает, что с ним делать. Для некоторых абонентов исключение A может указывать на восстанавливаемое условие. Для других абонентов это может указывать на ошибку. И из того, что я сказал выше, должно быть ясно, что если исключение указывает на ошибку, оно не должно быть поймано .

И если вам интересно, что это значит для блока обработки исключений Microsoft Enterprise Library : да, он довольно сломан. Они сообщают вам catch (Exception x), а затем решают, следует ли свергнуть, основываясь на вашей политике; слишком поздно - блоки finally к этому моменту уже выполнены. Не делай этого.

1 голос
/ 20 марта 2009

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

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

например

А звонит, Б звонит, С (выбрасывает исключение)

B ловит / отбрасывает

А ловит.

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

В общем, вы почти НИКОГДА не захотите исключения, чтобы убить вашу программу. Лучшая практика - ловить «исключение» и грациозно выходить. Это позволяет сохранить любую открытую в настоящее время информацию и высвободить используемые ресурсы, чтобы они не были повреждены. Если вы намереваетесь выйти, вы можете создать свой собственный информационный отчет 'core-dump', который будет содержать информацию о том, что вы делали, когда поймали фатальное исключение.

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

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

РЕДАКТИРОВАТЬ

Если вы пишете библиотеку, вам нужно заранее выбрать, будет ли ваша функция генерировать исключение или она будет безопасна. В этих случаях иногда вы генерируете исключение и не знаете, поймает ли его вызывающая сторона. Но в этом случае перехватывать это не ваша ответственность, если API-интерфейс объявляет, что функция может генерировать исключения. (Я ищу слово, которое означает «может вызвать исключение» ... кто-нибудь знает, что это?

1 голос
/ 20 марта 2009

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

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

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

Вы когда-нибудь НЕ поймаете исключение?

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

0 голосов
/ 03 января 2011

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

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

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

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

0 голосов
/ 20 марта 2009

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

И

Где-то вдоль линии я обнаружил, что состояние приложения стало несовместимым.

И

Система (пока) не знает, как исправить несоответствие и корректно восстановиться

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

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

...