finally 블록에서 예외가 발생하면 정확히 어떻게됩니까?
특히, 예외가 finally 블록의 중간에 발생하면 어떻게됩니까? 이 블록의 나머지 부분 (이후)이 호출됩니까?
예외가 위쪽으로 전파된다는 것을 알고 있습니다.
finally 블록에서 예외가 발생하면 정확히 어떻게됩니까?
특히, 예외가 finally 블록의 중간에 발생하면 어떻게됩니까? 이 블록의 나머지 부분 (이후)이 호출됩니까?
예외가 위쪽으로 전파된다는 것을 알고 있습니다.
답변:
finally 블록에서 예외 가 발생 하면 정확히 어떻게됩니까?
그 예외는 계속해서 전파되고 더 높은 수준에서 처리 될 수 있습니다.
최종 차단은 예외가 발생한 지점을 넘어 완료 되지 않습니다 .
이전 예외를 처리하는 동안 finally 블록이 실행 된 경우 첫 번째 예외가 손실됩니다.
C # 4 언어 사양 § 8.9.5 : finally 블록에서 다른 예외가 발생하면 현재 예외 처리가 종료됩니다.
ThreadAbortException
, 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에서 도달 할 수없는 코드가 감지되었음을 경고합니다).
try 블록에서 발생한 내부 catch 블록 처리 예외입니다. 이너 드디어 차단 finally 블록에서 발생한 외부 catch 블록 처리 예외 외부는 마지막으로 차단
추가 비고
Michael Damatov가 지적했듯이 try
(내부) 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");
}
}
}
출력에서 볼 수 있듯이 내부 예외는 "손실"입니다 (예 : 무시).
이너 드디어 차단 finally 블록에서 발생한 외부 catch 블록 처리 예외 외부는 마지막으로 차단
finally
블록은 (거의) 항상 실행됩니다.이 경우에도 내부 finally 블록에 대해서도 유지됩니다 (샘플 프로그램을 직접 시도하십시오 (복구 할 수없는 경우에는 마지막으로 블록이 실행되지 않습니다) 예외, 예를 들어 EngineExecutionException
, 그러나이 경우 프로그램은 즉시 종료됩니다)
보류중인 예외가있는 경우 ( try
블록이 finally
있지만 아니오 인 경우 catch
) 새 예외가 해당 예외를 대체합니다.
보류중인 예외가 없으면 finally
블록 외부에서 예외를 throw하는 것처럼 작동합니다 .
catch
.
원래 예외 가 더 중요한 경우 "원래 예외"( try
블록 에서 발생)를 저장 하고 "최종 예외"(블록 에서 발생)를 저장하는 빠른 (그리고 명백한) 스 니펫 finally
:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
위의 코드가 실행되면 "Original Exception"이 호출 스택을 전파하고 "Finally Exception"이 손실됩니다.
예외로 인해 결코 열리지 않은 스트림을 닫으려고하는 동안 오류를 잡기 위해이 작업을 수행해야했습니다.
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);
}
}
몇 달 전에 저는 이런 식으로 직면했습니다.
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"블록 실행은 예외가 발생한 지점에서 중지됩니다.
예외가 있는지 여부에 관계없이 "최종"블록의 실행이 보장됩니다.
try 블록에서 예외가 발생한 후 "finally"블록이 실행되는 경우,
그 예외가 처리되지 않으면
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");
}
}
}