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

Можно ли обнаружить, произошло ли исключение, прежде чем я ввел блок finally?

В Java есть ли элегантный способ определить, произошло ли исключение до запуска блока finally? При работе с операторами "close()" обычно требуется обработка исключений в блоке finally. В идеале мы хотели бы сохранить оба исключения и распространять их (поскольку оба они могут содержать полезную информацию). Единственный способ, которым я могу это сделать, - иметь переменную за пределами области try-catch-finally, чтобы сохранить ссылку на возникшее исключение. Затем распространяйте "сохраненное" исключение с любым, что происходит в блоке finally.

Есть ли более элегантный способ сделать это? Может быть, вызов API, который откроет это?

Вот пример грубого кода, о котором я говорю:

Throwable t = null; 
try {   
   stream.write(buffer); 
} catch(IOException e) {
    t = e;   //Need to save this exception for finally
    throw e;
} finally {   
    try {
       stream.close();   //may throw exception
   } catch(IOException e) {
      //Is there something better than saving the exception from the exception block?
      if(t!=null) {
         //propagate the read exception as the "cause"--not great, but you see what I mean.
         throw new IOException("Could not close in finally block: " + e.getMessage(),t);
      } else {
         throw e;  //just pass it up
      }    
   }//end close
}

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

Может быть что-то вроде Thread.getPendingException() или что-то подобное? На самом деле, есть ли элегантное решение на других языках?

Этот вопрос фактически возник из комментариев в другом вопросе, который вызвал интересный вопрос.

4b9b3361

Ответ 1

Ваша идея об установке переменной вне области try/catch/наконец верна.

Нельзя одновременно распространять более одного исключения.

Ответ 2

Вместо того, чтобы использовать логический флаг, я бы сохранил ссылку на объект Exception. Таким образом, у вас будет не только возможность проверить, произошло ли исключение (объект будет пустым, если не возникло исключение), но вы также получите доступ к самому объекту исключения в своем блоке finally, если произошло исключение. Вам просто нужно запомнить, чтобы установить объект ошибки во всех ваших блоках catch (iff rethrowing the error).

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

finally (Exception main_exception)
{
    try
    {
        //cleanup that may throw an error (absolutely unpredictably)
    }
    catch (Exception err)
    {
        //Instead of throwing another error,
        //just add data to main exception mentioning that an error occurred in the finally block!
        main_exception.Data.Add( "finally_error", err );
        //main exception propagates from finally block normally, with additional data
    }
}

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

Ответ 3

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

Ответ 4

Использовать протоколирование...

try {   
   stream.write(buffer); 
} catch(IOException ex) {
    if (LOG.isErrorEnabled()) { // You can use log level whatever you want
        LOG.error("Something wrong: " + ex.getMessage(), ex);
    }
    throw ex;
} finally {   
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException ex) {
            if (LOG.isWarnEnabled()) {
                LOG.warn("Could not close in finally block", ex);
            }
        }
    }
}

Ответ 5

В vb.net можно использовать оператор "Catch... When", чтобы захватить исключение локальной переменной без необходимости ее поймать. Это имеет ряд преимуществ. Среди них:

  • Если ничто не собирается "в конечном счете" поймать исключение, ловушка необработанного исключения будет запущена с места первоначального исключения. Гораздо приятнее, чем наличие ловушки отладчика при последнем реверберации, тем более что информация, которая может понадобиться для отладки, еще не вышла за рамки или была замечена "окончательными" утверждениями.
  • Хотя ретронирование не очистит трассировку стека, как это делает "Throw Ex", он все равно будет часто смещать трассировку стека. Если исключение не будет обнаружено, трассировка стека будет чистой.

Поскольку эта функция не поддерживается в vb, может быть полезно написать оболочку vb для реализации кода на C (например, с помощью MethodInvoker и Action (Of Exception), выполнить MethodInvoker в "Try" и Action в "Наконец".

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