Каковы плюсы и минусы проверенного исключения?

Вы предпочитаете проверенную обработку исключений, например, в Java или неконтролируемую обработку исключений, как на С# и почему?

Ответ 1

Мех.

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

doSomething();
try
{
  somethingThrowsCheckedException();
}
catch(ThatCheckedException)
{ }
doSomethingElse();    

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

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

public void itMightThrow() throws Exception1, Exception2, Exception3, Exception4, // ...
Exception12, Exception13, /* ... */ Exception4499379874
{
  // body
}

Я преувеличиваю? Лишь слегка.

Edit:

Тем не менее, одна вещь, которую я предпочитаю в С# над Java, когда дело доходит до обработки исключений, не имеет ничего общего с проверенными исключениями (я могу получить это, если я поеду с SpeС# в любом случае). Нет, мне нравится, что трассировка стека в С# заполняется, когда вы генерируете исключение, а не когда вы создаете экземпляр, как в Java.

Изменить 2: Это для комментаторов @Yishai, @Eddie, @Bill K:

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

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

private MyException NewException(string message)
{
   MyException e = new MyException(message);
   Logger.LogException(message, e);
   return e;
}

// and elsewhere...
if(mustThrow)
{
   throw NewException("WHOOOOPSIEE!");
}

Это трюк, который вы не можете сделать на Java без использования метода NewException в трассировке стека.

Ответ 2

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

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

Ответ 3

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

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

Таким образом, на практике проверенные исключения были перенесены из двух вещей:

  • Не все приложения, написанные на Java, нуждаются в такой надежности. Флаг уровня компилятора, чтобы отключить проверенные исключения, был бы приятным - хотя это может привести к злоупотреблению проверенными исключениями API, когда их разработчики работают с установленным флагом, чтобы отключить их. После размышления о более эффективном объединении здесь, мое текущее мышление заключается в том, что предупреждение о компиляторе является лучшим показателем здесь. Если отмеченные исключения были предупреждениями компилятора, включая предупреждение компилятора, если один из них был проигнорирован несколькими слоями (так что тот факт, что один был проигнорирован, будет скомпилирован в класс), так что вызывающий пользователь, по крайней мере, знал бы, чтобы поймать исключение, даже если он не смог бы " не знаю, какой из них, тогда те, кто не заботится, игнорируют предупреждение компилятора, а те, кто этого не сделает, не будут вынуждены писать код обработки ошибок, которые они не собираются компилировать.
  • Цепочка исключений заняла слишком много времени (версия 1.4), чтобы представить. Отсутствие цепочки исключений вызвало много вредных привычек для развития на ранней стадии, а не только для всех:

    throw new RuntimeException (e);

когда они не знали, что делать.

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

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

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

Я думаю, что обе версии страдают от непринятой точки, что, когда эти замечания были сделаны, уже было принято, что правильный способ справиться с проверенным исключением, перемещающимся на следующий уровень, - это обернуть другое проверенное исключение, соответствующее абстракции вашего API. Например, если у вас есть API-интерфейс хранения, который может обрабатывать исключения IOException, SQLException или XML, правильно описанный API скроет эти различия за общим исключением PersistanceException или что-то в этом роде.

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

  • Versioning. Поэтому разработчик разработал против вашего API баз данных, думая, что они поймали и обработали соответствующие исключения (скажем, DatabaseException), а затем вы решите в следующей версии добавить NetworkException для захвата проблем связи на уровне сети с базой данных. Теперь вы просто сломали всю совместимость с существующим кодом, и компилятор даже не пожаловаться на это. Каждый может обнаружить это при регрессионном тестировании, если им повезет.
  • Масштабируемость. В решении С#, если три уровня API вниз доступны для доступа к энергозависимому ресурсу, вы полностью полагаетесь на документацию API, потому что компилятор не скажет вам об этом.

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

Аргумент заканчивается кипением (для меня все равно): Не беспокойтесь об исключениях, все может пойти не так, как надо, и просто создать catch-all.

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

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

Ответ 4

Я никогда не использовал Java, но так как я читал

Я совершенно уверен, что мне не нравятся проверенные исключения (в текущей реализации).

Ниже перечислены два основных момента.

Versionability

Андерс Хейлсберг: Начните с версий, потому что проблемы довольно легко увидеть. Скажем, я создаю метод foo, который объявляет, что он генерирует исключения A, B и C. Во второй версии foo я хочу добавить кучу функций, и теперь foo может выбрасывать исключение D. Это изменение для меня добавьте D в предложение throws этого метода, потому что существующий вызывающий объект этого метода почти наверняка не обработает это исключение.

Добавление нового исключения в предложение throws в новой версии прерывает клиентский код. Это похоже на добавление метода к интерфейсу. После публикации интерфейса он практически неизменен, поскольку любая его реализация может иметь методы, которые вы хотите добавить в следующую версию. Поэтому вам нужно создать новый интерфейс. Аналогично исключениям, вам нужно будет создать целый новый метод foo2, который выдает больше исключений, или вам придется поймать исключение D в новом foo и преобразовать D в A, B или C.

Масштабируемость

Андерс Хейлсберг: Проблема масштабируемости несколько связана с проблемой работоспособности. В небольших проверенных исключениях очень заманчиво. В небольшом примере вы можете показать, что вы действительно проверили, что вы поймали FileNotFoundException, и не так ли? Ну, это прекрасно, когда вы просто называете один API. Проблема начинается, когда вы начинаете строить большие системы, где вы разговариваете с четырьмя или пятью различными подсистемами. Каждая подсистема выбрасывает от четырех до десяти исключений. Теперь, каждый раз, когда вы поднимаетесь по лестнице агрегации, у вас есть эта экспоненциальная иерархия ниже вас за исключениями, с которыми вам приходится иметь дело. В итоге вы должны объявить 40 исключений, которые вы можете бросить. И как только вы суммируете это с другой подсистемой, у вас есть 80 исключений из предложения throws. Это просто воздушные шары из-под контроля.

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

Ответ 5

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

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

Если вы хотите создать 5-9 надежный код, вам нужно знать, какие исключения могут быть выбраны из кода, который вы вызываете, поэтому вы можете рассуждать о том, что может быть восстановлено, и что должно заставить вас отказаться от того, что вы делаете, Если С#, вы можете сделать это, но это связано с большим количеством проб и ошибок, пока вы не увидите все возможные исключения, которые могут быть выброшены. Или вы просто перехватываете Exception и делаете все возможное.

Есть плюсы и минусы для обоих подходов - Java и С#. Разумные аргументы могут быть сделаны в пользу как, так и против обоих. Опять же, в сети, я предпочитаю подход, выбранный Java, но если бы я переписывал Java сегодня, я бы изменил API, чтобы изменить некоторые проверенные исключения в исключениях времени выполнения. Java API несовместим с использованием проверенных исключений. И, как сказал кто-то другой, для создания цепочки исключений потребовалось слишком много времени, чтобы отображаться как стандартная функция API и часть JVM.

Однако обвинения, которые лежат на ногах проверенных исключений, слишком часто попадают в категорию "ленивые программисты злоупотребляют этой функцией языка". Это правда. Но это верно для многих языков и их особенностей. Аргумент "ленивого программиста" является слабым.

Позвольте адресовать основные жалобы, которые не попадают в ведро "ленивого программиста":

  • Версии > - да, выброс нового Exception в новой версии вашего кода приведет к сломанию компиляции для клиентов, которые слепо упадут в ваш новый JAR файл. IMO, это хорошая вещь (до тех пор, пока у вас есть веская причина бросить дополнительное проверенное исключение), потому что клиенты вашей библиотеки должны рассуждать о том, что им нужно делать с этим изменением поведения, Если все не отмечено, ваши клиенты не обязательно имеют ключ (пока не произойдет исключение), что ваше поведение изменилось. Если вы меняете поведение своего кода, то разумно, чтобы ваши клиенты знали об этом. Вы когда-нибудь обновлялись до новой версии сторонней библиотеки только для того, чтобы ее поведение было незаметно изменено, и теперь ваша программа нарушена? Если вы изменяете поведение в вашей библиотеке, вы должны отключить автоматическую совместимость с клиентами, используя более ранние версии вашей библиотеки.

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

Я думаю, что в большинстве случаев проверенное исключение - пустая трата времени. Они захватывают такие вещи, как антипаттерн, упоминаемый randolpho, или обширное создание пользовательских исключений, чтобы отделить вашу реализацию от использованных библиотек. Beeing, избавившись от этой "функции", позволяет сосредоточиться на том, что вы хотите сделать.

Ответ 10

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