예외 처리
다음 지침은 사용자 라이브러리에서 예외를 올바로 처리할 수 있도록 하는 데 도움이 됩니다.
프레임워크 코드에서 System.Exception, System.SystemException 등과 같은 일반적인 예외를 catch하여 오류를 처리해서는 안 됩니다.
예외는 예외를 catch하여 다시 throw하거나 다른 스레드로 예외를 전송하려는 경우에만 catch할 수 있습니다. 다음 코드 예제에서는 잘못된 예외 처리를 보여 줍니다.
Public Class BadExceptionHandlingExample1
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
' Handle the exception and
' continue executing.
End Try
End Sub
End Class
public class BadExceptionHandlingExample1
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
// Handle the exception and
// continue executing.
}
}
}
public ref class BadExceptionHandlingExample1
{
public:
void DoWork()
{
// Do some work that might throw exceptions.
}
void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception^ e)
{
// Handle the exception and
// continue executing.
}
}
};
응용 프로그램 코드에서 System.Exception, System.SystemException 등과 같은 일반적인 예외를 catch하여 오류를 처리해서는 안 됩니다. 응용 프로그램의 오류는 처리할 수도 있지만 이러한 경우는 거의 발생하지 않습니다.
응용 프로그램이 예기치 않은 상태 또는 악용될 수 있는 상태를 발생시킬 수 있는 예외를 처리해서는 안 됩니다. 예외의 가능한 원인을 모두 예측할 수 없고 악성 코드가 결과 응용 프로그램 상태를 악용할 수 없는 것으로 확인할 수 없는 경우 응용 프로그램에서 예외를 처리하지 않고 종료될 수 있도록 해야 합니다.
예외를 전송하기 위해 catch하는 경우 특수 예외를 제외하지 않습니다.
catch 절에 일련의 특수 예외를 만드는 대신, 올바르게 처리할 수 있는 예외만 catch해야 합니다. 처리할 수 없는 예외는 일반 예외 처리기에서 특수한 경우로 처리되어서는 안 됩니다. 다음 코드 예제에서는 특수 예외를 다시 throw할 때 해당 예외를 잘못 테스트하는 것을 보여 줍니다.
Public Class BadExceptionHandlingExample2
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
If TypeOf e Is StackOverflowException Or _
TypeOf e Is OutOfMemoryException Then
Throw
End If
End Try
End Sub
End Class
public class BadExceptionHandlingExample2
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
if (e is StackOverflowException ||
e is OutOfMemoryException)
throw;
// Handle the exception and
// continue executing.
}
}
}
public ref class BadExceptionHandlingExample2
{
public:
void DoWork()
{
// Do some work that might throw exceptions.
}
void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception^ e)
{
if (e->GetType() == StackOverflowException::typeid ||
e->GetType() == OutOfMemoryException::typeid)
throw;
// Handle the exception and
// continue executing.
}
}
};
해당 컨텍스트에서 특수 예외가 throw되는 이유를 알고 있는 경우 해당 예외를 catch할 수 있습니다.
복구할 수 있는 예외만 catch해야 합니다. 예를 들어, 존재하지 않는 파일을 열려고 시도하여 발생하는 FileNotFoundException은 응용 프로그램으로 처리할 수 있습니다. 응용 프로그램은 사용자에게 문제를 전달하여 사용자가 다른 파일 이름을 지정하거나 파일을 만들게 할 수 있습니다. ExecutionEngineException을 생성하는 파일 열기 요청의 경우 근본적인 예외 원인을 알 수 없고 응용 프로그램이 계속 실행해도 안전한지 확신할 수 없으므로 처리해서는 안 됩니다.
catch를 남용하지 않습니다. 예외는 일반적으로 호출 스택 위쪽으로 전파되어야 합니다.
올바르게 처리할 수 없는 예외를 catch하는 경우 중요한 디버깅 정보가 표시되지 않습니다.
정리 코드에는 try-catch 대신 try-finally를 사용합니다. 올바르게 작성된 예외 코드의 경우 try-finally가 try-catch보다 일반적입니다.
catch 절을 사용하면 예를 들어, 사소한 오류를 기록하여 예외를 처리할 수 있습니다. finally 절을 사용하면 예외 throw 여부에 관계없이 정리 코드를 실행할 수 있습니다. 데이터베이스 연결 또는 스트림과 같이 비용이 많이 소요되거나 제한된 리소스를 할당하는 경우 finally 블록 내부에 해당 리소스를 해제하는 코드를 삽입해야 합니다.
예외를 catch하거나 다시 throw하는 경우 비어 있는 throw를 사용하는 것이 더 좋습니다. 이렇게 하면 예외 호출 스택을 가장 효과적으로 유지할 수 있습니다.
다음 코드 예제에서는 예외를 throw할 수 있는 메서드를 보여 줍니다. 이 메서드는 뒷부분의 예제에서 참조됩니다.
Public Sub DoWork(ByVal anObject As Object)
' Do some work that might throw exceptions.
If (anObject = Nothing) Then
Throw New ArgumentNullException("anObject", "Specify a non-null argument.")
End If
' Do work with o.
End Sub
public void DoWork(Object anObject)
{
// Do some work that might throw exceptions.
if (anObject == null)
{
throw new ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
public:
void DoWork(Object^ anObject)
{
// Do some work that might throw exceptions.
if (anObject == nullptr)
{
throw gcnew ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
다음 코드 예제에서는 예외를 catch하는 것과 예외를 다시 throw할 때 해당 예외를 잘못 지정하는 것을 보여 줍니다. 이러한 경우 스택 추적 시 DoWork 메서드가 아닌 다시 throw하는 위치를 오류 위치로 가리킵니다.
Public Sub MethodWithBadCatch(ByVal anObject As Object)
Try
DoWork(anObject)
Catch e As ArgumentNullException
System.Diagnostics.Debug.Write(e.Message)
' This is wrong.
Throw e
' Should be this:
' throw
End Try
End Sub
public void MethodWithBadCatch(Object anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException e)
{
System.Diagnostics.Debug.Write(e.Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
void MethodWithBadCatch(Object^ anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException^ e)
{
System::Diagnostics::Debug::Write(e->Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
매개 변수가 없는 catch 블록을 사용하여 CLS 규격이 아닌 예외, 즉 System.Exception에서 파생되지 않는 예외를 처리하지 않습니다. Exception에서 파생되지 않는 예외를 지원하는 언어는 이처럼 CLS 규격이 아닌 예외를 처리할 수 있습니다.
.NET Framework 버전 2.0은 Exception에서 파생된 클래스에서 CLS를 준수하지 않는 예외를 래핑합니다.
Portions Copyright 2005 Microsoft Corporation. All rights reserved.
Portions Copyright Addison-Wesley Corporation. All rights reserved.
디자인 지침에 자세한 내용은 참조를 "Framework 디자인 지침: 규칙, 숙어, 및 재사용에 대 한 패턴입니다.NET 라이브러리"도 서 Krzysztof Cwalina와 Brad Abrams, 게시 Addison-wesley, 2005.