Ответ 1
Ваша идея об установке переменной вне области try/catch/наконец верна.
Нельзя одновременно распространять более одного исключения.
В 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()
или что-то подобное? На самом деле, есть ли элегантное решение на других языках?
Этот вопрос фактически возник из комментариев в другом вопросе, который вызвал интересный вопрос.
Ваша идея об установке переменной вне области try/catch/наконец верна.
Нельзя одновременно распространять более одного исключения.
Вместо того, чтобы использовать логический флаг, я бы сохранил ссылку на объект 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 поймает исключение из своего собственного, вместо переписывает основное исключение, бросая новая ошибка (плохая) или просто игнорирование ошибки (также плохо), она может добавить ошибку в качестве дополнительных данных к исходной ошибке.
Вы всегда можете установить логический флаг в своих catch (-ах). Я не знаю никакого "скользкого" способа сделать это, но тогда я больше всего .Net-парень.
Использовать протоколирование...
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);
}
}
}
}
В vb.net можно использовать оператор "Catch... When", чтобы захватить исключение локальной переменной без необходимости ее поймать. Это имеет ряд преимуществ. Среди них:
Поскольку эта функция не поддерживается в vb, может быть полезно написать оболочку vb для реализации кода на C (например, с помощью MethodInvoker и Action (Of Exception), выполнить MethodInvoker в "Try" и Action в "Наконец".
Один интересный вопрос: возможно ли Catch-When увидеть исключение, которое в конечном итоге будет перезаписано исключением finally-clause. В некоторых случаях это может быть хорошо; в других случаях это может ввести в заблуждение. В любом случае, это то, о чем нужно знать.