programing

잡기 및 다시 던지기에 대한 모범 사례.NET 예외

javamemo 2023. 5. 5. 08:31
반응형

잡기 및 다시 던지기에 대한 모범 사례.NET 예외

예외를 포착하고 다시 던질 때 고려해야 할 가장 좋은 방법은 무엇입니까?나는 확실히 하고 싶습니다.Exception물의InnerException및 스택 추적이 보존됩니다.다음 코드 블록 간에 이를 처리하는 방식에 차이가 있습니까?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Vs:

try
{
    //some code
}
catch
{
    throw;
}

은 스추적보방다법것사입다니용는하음을을 입니다.throw;합니다.

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;으로 그 추적은 됩니다.throw ex;진술.

예외를 통해 예외를 통과할 수 있다고 가정할 때(권장 사항) Mike도 맞습니다.

칼 세긴전자책 프로그래밍의 기초에서 예외 처리에 대한 훌륭한 글을 남겼는데, 이것은 훌륭한 읽을거리입니다.

편집: 프로그래밍 기초 PDF에 대한 작업 링크.텍스트에서 "예외"를 검색하면 됩니다.

초기 예외와 함께 새 예외를 만들면 초기 스택 추적도 보존됩니다.

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
}

스택 추적은 54번 라인이 47번 라인에서 예외를 제기했음을 나타냅니다.

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를 보존하는 두 가지 옵션이 있습니다.

예외 호출.내부 보존 스택 추적

개인적인 방법이기 때문에 반사를 사용하여 호출해야 합니다.

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

나는 StackTrace 정보를 보존하기 위해 개인적인 방법에 의존하는 단점이 있습니다.의 향후 버전에서 변경할 수 있습니다.NET Framework.위의 코드 예제와 아래의 제안된 솔루션은 Fabrice MARGUERY 웹로그에서 추출되었습니다.

예외 호출.객체 데이터 설정

아래 기술은 InC#에 대한 답변으로 Anton Tykhyy에 의해 제안되었습니다. 어떻게 하면 스택 추적 질문을 잃지 않고 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그랬을 수도 있습니다.

아무도 사이의 차이점을 설명하지 않았습니다.ExceptionDispatchInfo.Capture( ex ).Throw() 원평.throw자, 여기 있습니다.하지만, 몇몇 사람들은 문제를 알아차렸습니다.throw.

은 탐된예외되돌완는방같전다다습니음과법은한지리를 사용하는 입니다.ExceptionDispatchInfo.Capture( ex ).Throw()으할 수 있습니다 4 순 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.
    }
}

3.

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 );
    }
}

는 "case 1"과 "case 2"의 줄가 있는 합니다.CallingMethod의 행 입니다.throw new Exception( "TEST" )선을

줄인 나은스, 케이신 3당게스의 스택 입니다. 여기서 소스 코드 라인 번호는CallingMethod의 행 입니다.throw에 콜. 은만에약이것▁the만에약▁call▁if▁that것이.throw new Exception( "TEST" )라인은 다른 작업에 의해 둘러싸여 있으며, 예외가 실제로 몇 번째 라인 번호로 던져졌는지 알 수 없습니다.

사례 4는 원래 예외의 줄 번호가 보존되기 때문에 사례 2와 유사하지만, 원래 예외의 유형을 변경하기 때문에 실제 되돌리기는 아닙니다.

예외를 에 다시 넣으려면 항상 "throw;"를 사용해야 합니다.그물,

를 참조하십시오. http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

기본적으로 MSIL(CIL)에는 "던지기"와 "다시 던지기"라는 두 가지 명령이 있습니다.

  • C#의 "throw ex;"는 MSIL의 "throw"로 컴파일됩니다.
  • C#의 "던지기;" - MSIL "다시 던지기"로!

기본적으로 "스스로 ex"가 스택 추적을 재정의하는 이유를 알 수 있습니다.

몇몇 사람들은 실제로 매우 중요한 점을 놓쳤습니다. '던지기'와 '던지기'는 같은 일을 할 수 있지만 예외가 발생한 라인인 중요한 정보를 제공하지는 않습니다.

다음 코드를 고려합니다.

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

'던지기' 또는 '던지기'를 수행하면 스택 추적을 얻을 수 있지만 line#은 #22가 되므로 어떤 라인이 예외를 던졌는지 정확히 파악할 수 없습니다(시도 블록에 코드가 1줄 또는 몇 줄만 있는 경우 제외).예외에서 예상되는 #17 줄을 얻으려면 원래 예외 스택 추적을 사용하여 새 예외를 던져야 합니다.

다음을 사용할 수도 있습니다.

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

그리고 던져진 모든 예외는 그것들을 처리하는 다음 단계로 거품이 일 것입니다.

저는 반드시 다음을 사용할 것입니다.

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

그러면 스택이 보존됩니다.

참고로 방금 테스트했는데 'throw'에 의해 보고된 스택 추적은 완전히 올바른 스택 추적이 아닙니다.예:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

스택 추적은 예외의 원점(보고된 라인 번호)을 정확하게 가리킵니다. 그러나 foo()에 대해 보고된 라인 번호는 스로우의 라인이며, 문장이므로 bar()에 대한 호출 중 어떤 것이 예외를 유발했는지 알 수 없습니다.

언급URL : https://stackoverflow.com/questions/22623/best-practices-for-catching-and-re-throwing-net-exceptions

반응형