[C#] .NET 예외를 포착하고 다시 발생시키는 모범 사례

예외를 잡아서 다시 던질 때 고려해야 할 모범 사례는 무엇입니까? 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 TykhyyC #에 대한 답변 으로 제안한 것으로 스택 추적 질문 을 잃지 않고 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와 유사하지만 원래 예외의 유형을 변경하기 때문에 실제로는 다시 발생하지 않습니다.