Что произойдет, если блок finally выдает исключение?
Если блок finally выдает исключение, что ровно происходит?
в частности, что произойдет, если исключение брошено на полпути через блок finally. Вызываются ли остальные операторы (после) в этом блоке?
Я знаю, что исключения будут распространяться вверх.
11 ответов
Если блок finally выдает исключение what ровно происходит ?
это исключение распространяется и вверх, и будет (может) быть обработаны на более высоком уровне.
ваш блок finally будет не быть завершено за пределами точки, где создается исключение.
Если блок finally выполнялся во время обработки более раннего исключения, то это первое исключение теряется.
C# 4 Спецификация языка § 8.9.5: если блок finally создает другое исключение, обработка текущего исключения завершается.
для таких вопросов я обычно открываю пустой проект консольного приложения в Visual Studio и пишу небольшой пример программы:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
catch (Exception ex)
{
Console.WriteLine("Inner catch block handling {0}.", ex.Message);
throw;
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
при запуске программы вы увидите точный порядок, в котором catch
и finally
блоки выполняются. Обратите внимание, что код в блоке finally после создания исключения не будет выполнен (фактически, в этом примере Visual Studio даже предупредит вас, что он обнаружил недостижимость код):
Inner catch block handling exception thrown from try block. Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
Дополнительная Информация
как Майкл Damatov указал, исключение из try
блок будет "съеден", если вы не обработаете его в (внутреннем) catch
блок. На самом деле, в приведенном выше примере повторное исключение не отображается во внешнем блоке catch. Чтобы сделать это еще более ясным, посмотрите на следующий слегка измененный образец:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
как вы можете видеть на выходе, внутреннее исключение "потеряно" (т. е. игнорируется):
Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
если есть ожидающее исключение (когда try
блока finally
а не catch
), новое исключение заменяет одна.
если нет ожидающего исключения, он работает так же, как и исключение за пределами finally
блок.
быстрый (и довольно очевидный) фрагмент для сохранения "оригинального исключения" (брошенного в try
блок) и пожертвовать "наконец исключением" (брошено в finally
блок), в случае, если оригинал более важен для вас:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
когда код выше выполняется, " исходное исключение "распространяется вверх по стеку вызовов, и" наконец исключение " теряется.
мне пришлось сделать это, чтобы поймать ошибку, пытаясь закрыть поток, который никогда не был открыт из-за исключения.
errorMessage = string.Empty;
try
{
byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);
webRequest = WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "text/xml;charset=utf-8";
webRequest.ContentLength = requestBytes.Length;
//send the request
using (var sw = webRequest.GetRequestStream())
{
sw.Write(requestBytes, 0, requestBytes.Length);
}
//get the response
webResponse = webRequest.GetResponse();
using (var sr = new StreamReader(webResponse.GetResponseStream()))
{
returnVal = sr.ReadToEnd();
sr.Close();
}
}
catch (Exception ex)
{
errorMessage = ex.ToString();
}
finally
{
try
{
if (webRequest.GetRequestStream() != null)
webRequest.GetRequestStream().Close();
if (webResponse.GetResponseStream() != null)
webResponse.GetResponseStream().Close();
}
catch (Exception exw)
{
errorMessage = exw.ToString();
}
}
Если webRequest был создан, но ошибка подключения произошла во время
using (var sw = webRequest.GetRequestStream())
затем, наконец, поймает исключение, пытающееся закрыть соединения, которые он считал открытыми, потому что был создан webRequest.
Если, наконец, не было try-catch внутри, этот код вызовет необработанное исключение в то время как очистка webRequest
if (webRequest.GetRequestStream() != null)
оттуда код выйдет без правильной обработки ошибки, которая произошла и, следовательно, вызывает проблемы для вызывающего метода.
надеюсь, это поможет в качестве примера
создание исключения, когда другое исключение активно, приведет к замене первого исключения вторым (более поздним) исключением.
вот некоторый код, который иллюстрирует, что происходит:
public static void Main(string[] args)
{
try
{
try
{
throw new Exception("first exception");
}
finally
{
//try
{
throw new Exception("second exception");
}
//catch (Exception)
{
//throw;
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
- запустить код и вы увидите "второе исключение"
- раскомментируйте операторы try и catch, и вы увидите "первое исключение"
- также раскомментируйте throw; оператор, и вы увидите " второе исключение" снова.
несколько месяцев назад я тоже столкнулся с чем-то подобным,
private void RaiseException(String errorMessage)
{
throw new Exception(errorMessage);
}
private void DoTaskForFinally()
{
RaiseException("Error for finally");
}
private void DoTaskForCatch()
{
RaiseException("Error for catch");
}
private void DoTaskForTry()
{
RaiseException("Error for try");
}
try
{
/*lacks the exception*/
DoTaskForTry();
}
catch (Exception exception)
{
/*lacks the exception*/
DoTaskForCatch();
}
finally
{
/*the result exception*/
DoTaskForFinally();
}
чтобы решить такую проблему, я сделал класс утилиты, как
class ProcessHandler : Exception
{
private enum ProcessType
{
Try,
Catch,
Finally,
}
private Boolean _hasException;
private Boolean _hasTryException;
private Boolean _hasCatchException;
private Boolean _hasFinnallyException;
public Boolean HasException { get { return _hasException; } }
public Boolean HasTryException { get { return _hasTryException; } }
public Boolean HasCatchException { get { return _hasCatchException; } }
public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
public Dictionary<String, Exception> Exceptions { get; private set; }
public readonly Action TryAction;
public readonly Action CatchAction;
public readonly Action FinallyAction;
public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
{
TryAction = tryAction;
CatchAction = catchAction;
FinallyAction = finallyAction;
_hasException = false;
_hasTryException = false;
_hasCatchException = false;
_hasFinnallyException = false;
Exceptions = new Dictionary<string, Exception>();
}
private void Invoke(Action action, ref Boolean isError, ProcessType processType)
{
try
{
action.Invoke();
}
catch (Exception exception)
{
_hasException = true;
isError = true;
Exceptions.Add(processType.ToString(), exception);
}
}
private void InvokeTryAction()
{
if (TryAction == null)
{
return;
}
Invoke(TryAction, ref _hasTryException, ProcessType.Try);
}
private void InvokeCatchAction()
{
if (CatchAction == null)
{
return;
}
Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
}
private void InvokeFinallyAction()
{
if (FinallyAction == null)
{
return;
}
Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
}
public void InvokeActions()
{
InvokeTryAction();
if (HasTryException)
{
InvokeCatchAction();
}
InvokeFinallyAction();
if (HasException)
{
throw this;
}
}
}
и используется вот так
try
{
ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
handler.InvokeActions();
}
catch (Exception exception)
{
var processError = exception as ProcessHandler;
/*this exception contains all exceptions*/
throw new Exception("Error to Process Actions", exception);
}
но если вы хотите использовать параметры и типы возвращаемых это другая история
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
способ обработки исключений, создаваемых CodeA и CodeB, одинаковый.
исключение в finally
блок не имеет ничего особенного, рассматривайте его как исключение броска кодом B.
исключение распространяется вверх и должно обрабатываться на более высоком уровне. Если исключение не обрабатывается на более высоком уровне, приложение аварийно завершает работу. Выполнение блока "finally" останавливается в точке, где создается исключение.
независимо от того, есть ли исключение или нет, блок "finally" гарантированно выполняется.
если блок" finally " выполняется после того, как в try произошло исключение блок
и если это исключение не обрабатывается
и если блок finally выдает исключение
затем теряется исходное исключение, возникшее в блоке try.
public class Exception
{
public static void Main()
{
try
{
SomeMethod();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void SomeMethod()
{
try
{
// This exception will be lost
throw new Exception("Exception in try block");
}
finally
{
throw new Exception("Exception in finally block");
}
}
}