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

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

С# и Java (и слишком многие другие) имеют множество типов некоторых типов "переполнения", которые мне вообще не нравятся (например, type.MaxValue + type.SmallestValue == type.MinValue например: int.MaxValue + 1 == int.MinValue).

Но, увидев мою порочную природу, я добавлю оскорбление этой травмы, расширив это поведение, скажем, тип Overridden DateTime. (Я знаю, что DateTime запечатан в .NET, но для этого примера Im использует псевдоязык, который точно такой же, как С#, за исключением того факта, что DateTime не запечатан).

Переопределенный метод Add:

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

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

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

ИМХО "Никогда не использовать их для регулярного потока программ", все, кажется, не так хорошо подстраиваются, поскольку сила этой реакции может оправдаться.

Или я ошибаюсь?

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

  • Очистить
  • Почитайте договор своего метода.

Стреляй меня.

Ответ 1

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

У меня есть.

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

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

Моя точка: , если вы используете исключения для нормальных ситуаций, как вы обнаруживаете необычные (то есть исключительные) ситуации?

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

Ответ 2

Исключения в основном являются нелокальными операторами goto со всеми последними последними. Использование исключений для управления потоком нарушает принцип наименьшего удивления, делает программы трудными для чтения (помните, что программы сначала написаны для программистов).

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

Однако некоторые языки (в частности, Python) используют исключения как конструкции управления потоком. Например, итераторы создают исключение StopIteration, если нет дополнительных элементов. Даже стандартные языковые конструкции (например, for) полагаются на это.

Ответ 3

Мое правило:

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

Проблема, которую я вижу с исключениями, имеет чисто синтаксическую точку зрения (я уверен, что накладные расходы на производительность минимальны). Мне не нравятся блоки try-blocks повсюду.

Возьмем этот пример:

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

.. Другим примером может быть, когда вам нужно назначить что-то дескриптору с помощью factory, и что factory может генерировать исключение:

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

Су, лично, я думаю, что вы должны соблюдать исключения для редких условий ошибок (из памяти и т.д.) и использовать returnvalues ​​(valueclasses, structs или enums) вместо проверки своей ошибки.

Надеюсь, я правильно понял ваш вопрос:)

Ответ 4

Первая реакция на множество ответов:

вы пишете для программистов и принцип наименьшего удивления

Конечно! Но если все еще не ясно, все время.

Не удивительно, например: divide (1/x) catch (divisionByZero) более ясен, чем любой, если я (Конрад и другие). Тот факт, что такого рода программирование не ожидается, является чисто условным и, действительно, все еще актуальным. Может быть, в моем примере, если бы было яснее.

Но DivisionByZero и FileNotFound понятнее, чем ifs.

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

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

исключения для нормальных ситуаций, как вы обнаруживаете необычные (то есть исключительные) ситуации?

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

Отладка по всем исключениям: то же самое, что просто делается иногда, потому что дизайн не использовать исключения распространен. Мой вопрос: почему это распространено в первую очередь?

Ответ 5

До исключений в Си были setjmp и longjmp которые можно было использовать для аналогичной развертки фрейма стека.

Затем этой же конструкции было дано имя: "Исключение". И большинство ответов полагаются на значение этого имени, чтобы спорить о его использовании, утверждая, что исключения предназначены для использования в исключительных условиях. Это никогда не было целью в оригинальном longjmp. Были только ситуации, когда нужно было разорвать поток управления на множество кадров стека.

Исключения немного более общие в том смысле, что вы можете использовать их в одном и том же фрейме стека. Это приводит к аналогиям с goto которые я считаю неправильными. Gotos - это тесно связанная пара (как и setjmp и longjmp). Исключения следуют из слабосвязанной публикации/подписки, которая намного чище! Поэтому их использование в одном и том же стековом кадре вряд ли то же самое, что использование goto s.

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

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

Мое любимое использование - это последовательность throw new Success() в длинный фрагмент кода, который пробует одно за другим, пока не найдет то, что ищет. Каждая вещь - каждая часть логики - может иметь произвольное вложение, поэтому break отсутствуют, как и любые виды тестов условий. Шаблон if-else является хрупким. Если я отредактирую else или испорчу синтаксис каким-либо другим способом, то это будет волосатая ошибка.

Использование throw new Success() линеаризует поток кода. Я использую локально определенные классы Success - проверено, конечно, - так что, если я забуду его перехватить, код не скомпилируется. И я не поймать еще один метод Success эс.

Иногда мой код проверяет одну вещь за другой и только успешно, если все в порядке. В этом случае у меня есть аналогичная линеаризация с использованием throw new Failure().

Использование отдельной функции портит естественный уровень компартментализации. Поэтому return решение не является оптимальным. Я предпочитаю иметь одну или две страницы кода в одном месте по когнитивным причинам. Я не верю в ультра тонко разделенный код.

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

Джош Блох широко обсуждает эту тему в Effective Java. Его предложения освещаются и должны также применяться к .Net(за исключением деталей).

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

Например, второй способ проще в использовании, чем первый:

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 * @throws AdditionException if addend1 or addend2 is less than or equal to zero
 */
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
  if( addend1 <= 0 ){
     throw new AdditionException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new AdditionException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 */
public int addPositiveNumbers(int addend1, int addend2) {
  if( addend1 <= 0 ){
     throw new IllegalArgumentException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new IllegalArgumentException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

В любом случае вам нужно проверить, чтобы вызывающий абонент правильно использовал ваш API. Но во втором случае вам требуется (неявно). Мягкие Исключения будут по-прежнему выдаваться, если пользователь не прочитал javadoc, но:

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

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

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

Ответ 9

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

Лучший способ сократить затраты на создание исключений - переопределить метод fillInStackTrace() следующим образом:

public Throwable fillInStackTrace() { return this; }

В таком исключении не будет заполнено стек.

Ответ 10

Я не вижу, как вы управляете потоком программы в коде, который вы указали. Вы никогда не увидите другого исключения, кроме исключения ArgumentOutOfRange. (Таким образом, ваше второе предложение catch никогда не пострадает). Все, что вы делаете, использует чрезвычайно дорогостоящий бросок, чтобы имитировать оператор if.

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

Ответ 11

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

Ответ 12

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

Например, когда я пытаюсь найти ошибку в VS, я обычно включаю "break on all exceptions". Если вы используете исключения для управления потоками, я буду регулярно ломаться в отладчике и буду продолжать игнорировать эти исключительные исключения, пока не дойду до реальной проблемы. Это, вероятно, приведет кого-то в бешенство!!

Ответ 13

Вот рекомендации, которые я описал в своем сообщении в блоге:

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

Ответ 14

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

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

Я помню из моих дней C многие функции возвращали коды ошибок вроде этого:

-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY

и др.

Неужели это менее читаемо, чем бросать и ловить исключение?

Ответ 15

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

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

Сказал иначе: просто потому, что вы можете не означает, что вы должны.

Ответ 16

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

В общем, если вы знаете, что существует высокая вероятность отказа, которую вы можете проверить... вы должны сделать чек... то есть if (obj!= null) obj.method()

В вашем случае я недостаточно хорошо знаком с библиотекой С#, чтобы узнать, имеет ли время дат простой способ проверить, не является ли временная метка вне пределов. Если это так, просто вызовите if (.isvalid(ts)) в противном случае ваш код в основном прекрасен.

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

Ответ 17

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

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

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

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

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

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

Ответ 18

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

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

Конечно, все еще есть соображения производительности, если вы подпрыгиваете вверх и вниз по стеку, как yo-yo, но это гораздо более общая идея, чем подход "oh crap, позволяет залог", что большинство исключений catch/throw системы.

Ответ 19

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

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

Пример: Microsoft Volta - это проект, позволяющий записывать веб-приложения в прямом .NET и позволять структуре заботиться о выясняя, какие биты нужно запускать там. Одним из следствий этого является то, что Volta должна быть в состоянии скомпилировать CIL для JavaScript, чтобы вы могли запускать код на клиенте. Однако есть проблема:.NET имеет многопоточность, JavaScript - нет. Итак, Volta реализует продолжение в JavaScript с использованием JavaScript Exceptions, а затем реализует .NET Threads, используя эти продолжения. Таким образом, приложения Volta, использующие потоки, могут быть скомпилированы для запуска в немодифицированном браузере - не требуется Silverlight.

Ответ 20

Одна эстетическая причина:

Попробуйте всегда с уловкой, тогда как if не нужно приходить с else.

if (PerformCheckSucceeded())
   DoSomething();

С try/catch он становится намного более подробным.

try
{
   PerformCheckSucceeded();
   DoSomething();
}
catch
{
}

Это слишком много строк кода.

Ответ 21

Как многие другие упоминали, принцип наименьшего удивления запретит чрезмерно использовать исключения для целей управления потоком. С другой стороны, ни одно правило не является на 100% правильным, и всегда есть те случаи, когда исключение - это "просто правильный инструмент" - как, например, goto, который отправляется в форме break и continue в таких языках, как Java, которые часто являются идеальным способом выпрыгнуть из сильно вложенных циклов, которые не всегда можно избежать.

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

В нем объясняется, как внутри jOOQ (библиотека абстракции SQL для Java) такие исключения иногда используются, чтобы прервать процесс рендеринга SQL раньше, выполняется некоторое "редкое" состояние.

Примерами таких условий являются:

  • Слишком много значений привязки. Некоторые базы данных не поддерживают произвольное количество значений привязки в своих операторах SQL (SQLite: 999, Ingres 10.1.0: 1024, Sybase ASE 15.5: 2000, SQL Server 2008: 2100). В этих случаях jOOQ прерывает фазу рендеринга SQL и переопределяет оператор SQL с помощью встроенных значений привязки. Пример:

    // Pseudo-code attaching a "handler" that will
    // abort query rendering once the maximum number
    // of bind values was exceeded:
    context.attachBindValueCounter();
    String sql;
    try {
    
      // In most cases, this will succeed:
      sql = query.render();
    }
    catch (ReRenderWithInlinedVariables e) {
      sql = query.renderWithInlinedBindValues();
    }
    

    Если мы явно извлекли значения привязки из запроса AST, чтобы подсчитывать их каждый раз, мы будем тратить ценные циклы CPU на те 99,9% запросов, которые не страдают от этой проблемы.

  • Некоторая логика доступна только косвенно через API, который мы хотим выполнить только "частично". Метод UpdatableRecord.store() генерирует оператор INSERT или UPDATE, в зависимости от внутренних флагов Record. Из "внешнего" мы не знаем, какая логика содержится в store() (например, оптимистическая блокировка, обработка прослушивателя событий и т.д.), Поэтому мы не хотим повторять эту логику, когда мы храним несколько записей в пакетный оператор, где мы хотим, чтобы store() генерировал только инструкцию SQL, а не выполнял ее. Пример:

    // Pseudo-code attaching a "handler" that will
    // prevent query execution and throw exceptions
    // instead:
    context.attachQueryCollector();
    
    // Collect the SQL for every store operation
    for (int i = 0; i < records.length; i++) {
      try {
        records[i].store();
      }
    
      // The attached handler will result in this
      // exception being thrown rather than actually
      // storing records to the database
      catch (QueryCollectorException e) {
    
        // The exception is thrown after the rendered
        // SQL statement is available
        queries.add(e.query());                
      }
    }
    

    Если бы мы внедрили логику store() в "повторно используемый" API, который можно настроить для необязательного выполнения SQL, мы будем изучать создание довольно сложного для использования, вряд ли повторно используемого API.

Заключение

В сущности, наше использование этих нелокальных goto просто соответствует тому, что [Мейсон Уилер] [5] сказал в своем ответе:

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

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

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

Ответ 22

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

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

Ответ 23

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

Ответ 24

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

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

  • Попросите метод вернуть "скрытый" флаг, который отличает нормальный возврат от исключения, и чтобы вызывающий объект проверял этот флаг и ветвь на "процедуру исключения", если он установлен. Эта процедура добавляет 1-2 инструкции к случаю без исключения, но относительно небольшая накладная, когда возникает исключение.

  • Укажите, чтобы вызывающая сторона размещала информацию об исключении или код с фиксированным адресом относительно уложенного обратного адреса. Например, с помощью ARM вместо использования инструкции "BL подпрограмма" можно использовать следующую последовательность:

        adr lr,next_instr
        b subroutine
        b handle_exception
    next_instr:
    

Чтобы нормально выйти, подпрограмма просто выполнила бы bx lr или pop {pc}; в случае аномального выхода подпрограмма вычитает 4 из LR перед выполнением возврата или использования sub lr,#4,pc (в зависимости от изменения ARM, режима выполнения и т.д.). Такой подход будет очень плохо работать, если вызывающий абонент не предназначен для приспособить его.

Язык или фреймворк, который использует проверенные исключения, может извлечь выгоду из того, что они обработаны с помощью механизма, такого как # 2 или # 3 выше, в то время как исключенные исключения обрабатываются с использованием # 1. Хотя реализация проверенных исключений в Java довольно неприятна, они не будут плохой концепцией, если бы существовало средство, с помощью которого сайт-вызов мог бы сказать, по существу: "Этот метод объявлен как металирование XX, но я не ожидаю его когда это делается, если это произойдет, сверните как" исключенное" исключение. В рамках, где проверенные исключения обрабатывались таким образом, они могли бы стать эффективным средством управления потоком для таких вещей, как методы разбора, которые в некоторых контекстах могут иметь высокая вероятность неудачи, но когда отказ должен возвращать принципиально различную информацию, кроме успеха. Однако я не знаю о каких-либо инфраструктурах, которые используют такой шаблон. Вместо этого более распространенной моделью является использование первого подхода выше (минимальная стоимость для нет -exception, но высокая стоимость при исключении исключений) для всех исключений.