예외를 잡아서 다시 던질 때 고려해야 할 모범 사례는 무엇입니까? Exception
객체 InnerException
와 스택 추적이 유지 되도록하고 싶습니다 . 다음 코드 블록이이를 처리하는 방식에 차이가 있습니까?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
대 :
try
{
//some code
}
catch
{
throw;
}
답변
스택 추적을 유지하는 방법은 throw;
This is valid of the
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
throw ex;
기본적으로 그 시점에서 예외를 던지는 것과 같으므로 스택 추적은 throw ex;
명령문을 발행하는 위치로만 이동 합니다.
예외가 예외를 전달할 수 있다고 가정하면 Mike 도 정확합니다 (권장).
Karl Seguin 은 프로그래밍 전자 책 기초 에서 예외 처리 에 대한 글 을 많이 썼습니다 .
편집 : 프로그래밍 기초에 대한 작업 링크 pdf. 텍스트에서 “예외”를 검색하십시오.
답변
초기 예외와 함께 새 예외를 throw하면 초기 스택 추적도 유지됩니다.
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
답변
실제로,이 throw
통계로 인해 StackTrace 정보가 보존되지 않는 상황이 있습니다. 예를 들어 아래 코드에서
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
StackTrace는 줄 54에서 예외가 발생했지만 줄 54에서 예외가 발생했음을 나타냅니다.
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
위에서 설명한 것과 같은 상황에서는 원래 StackTrace를 미리 배치하는 두 가지 옵션이 있습니다.
Exception.InternalPreserveStackTrace 호출
전용 메소드이므로 리플렉션을 사용하여 호출해야합니다.
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
StackTrace 정보를 보존하기 위해 개인 메서드에 의존하는 단점이 있습니다. 이후 버전의 .NET Framework에서 변경 될 수 있습니다. 위의 코드 예제와 아래 제안 된 솔루션은 Fabrice MARGUERIE 웹 블로그 에서 추출되었습니다. .
Exception.SetObjectData 호출
아래의 기술은 Anton Tykhyy 가 C #에 대한 답변 으로 제안한 것으로 스택 추적 질문 을 잃지 않고 InnerException 을 다시 던질 수있는 방법 입니다.
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
그러나 공개 메소드에만 의존한다는 장점이 있지만 다음 예외 생성자 (제 3자가 개발 한 일부 예외는 구현하지 않음)에 따라 다릅니다.
protected Exception(
SerializationInfo info,
StreamingContext context
)
필자의 상황에서는 사용중인 타사 라이브러리에서 발생한 예외 가이 생성자를 구현하지 않았기 때문에 첫 번째 접근 방식을 선택해야했습니다.
답변
의 경우 throw ex
본질적으로 새로운 예외가 발생하며 원래 스택 추적 정보가 누락됩니다. throw
선호되는 방법입니다.
답변
경험상 기본 Exception
개체를 잡거나 던지는 것을 피하는 것이 좋습니다. 이를 통해 예외에 대해 조금 더 똑똑해집니다. 다른 말로하면 SqlException
처리 코드가 문제가되지 않도록 명시 적으로 잡아야합니다 .NullReferenceException
.
그러나 실제 상황에서는 기본 예외를 포착 하고 기록 하는 것도 좋은 방법이지만 모든 것을 얻기 위해 걸어 다니는 것을 잊지 마십시오 InnerExceptions
.
답변
항상 “throw”를 사용해야합니다. .NET에서 예외를 다시 발생시키기 위해
이것을 참조하십시오
http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
기본적으로 MSIL (CIL)에는 “throw”와 “rethrow”라는 두 가지 명령이 있습니다.
- C #의 “전 던지기;” MSIL의 “throw”로 컴파일
- C #의 “throw;” -MSIL에 “다시 던져”!
기본적으로 “throw ex”가 스택 추적을 재정의하는 이유를 알 수 있습니다.
답변
아무도 ExceptionDispatchInfo.Capture( ex ).Throw()
와 일반 의 차이점을 설명하지 throw
않았으므로 여기에 있습니다. 그러나 일부 사람들은의 문제를 발견했습니다 throw
.
발견 된 예외를 다시 발생시키는 완전한 방법은 사용하는 것입니다 ExceptionDispatchInfo.Capture( ex ).Throw()
(.Net 4.5에서만 사용 가능).
아래에는 이것을 테스트하는 데 필요한 경우가 있습니다.
1.
void CallingMethod()
{
//try
{
throw new Exception( "TEST" );
}
//catch
{
// throw;
}
}
2.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
ExceptionDispatchInfo.Capture( ex ).Throw();
throw; // So the compiler doesn't complain about methods which don't either return or throw.
}
}
삼.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch
{
throw;
}
}
4.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
throw new Exception( "RETHROW", ex );
}
}
사례 1과 사례 2는 CallingMethod
메소드 의 소스 코드 라인 번호 가throw new Exception( "TEST" )
.
그러나 케이스 3은 CallingMethod
메소드 의 소스 코드 라인 번호 가 throw
호출 의 라인 번호 인 스택 추적을 제공합니다 . 이것은throw new Exception( "TEST" )
라인이 다른 오퍼레이션으로 둘러싸여 실제로 어떤 라인 번호에서 예외가 발생했는지 알 수 없다는 .
케이스 4는 원래 예외의 행 번호가 유지되기 때문에 케이스 2와 유사하지만 원래 예외의 유형을 변경하기 때문에 실제로는 다시 발생하지 않습니다.