답변:
스택 추적을 유지하는 방법은 throw;
This is valid of the
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
throw ex;
기본적으로 그 시점에서 예외를 던지는 것과 같으므로 스택 추적은 throw ex;
명령문을 발행하는 위치로만 이동 합니다.
예외가 예외를 전달할 수 있다고 가정하면 Mike 도 정확합니다 (권장).
Karl Seguin 은 프로그래밍 전자 책 기초 에서 예외 처리 에 대한 글 을 많이 썼습니다 .
편집 : 프로그래밍 기초에 대한 작업 링크 pdf. 텍스트에서 "예외"를 검색하십시오.
ExceptionDispatchInfo.Capture(ex).Throw(); throw;
.NET +4.5 stackoverflow.com/questions/57383/…
초기 예외와 함께 새 예외를 throw하면 초기 스택 추적도 유지됩니다.
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
AggregateException
집계 된 작업에 대한 예외에만 사용해야합니다. 예를 들어 CLR 의 ParallelEnumerable
및 Task
클래스에 의해 발생합니다 . 사용법은 아마도이 예제를 따라야합니다.
실제로,이 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
)
필자의 상황에서는 사용중인 타사 라이브러리에서 발생한 예외 가이 생성자를 구현하지 않았기 때문에 첫 번째 접근 방식을 선택해야했습니다.
경험상 기본 Exception
개체를 잡거나 던지는 것을 피하는 것이 좋습니다. 이를 통해 예외에 대해 조금 더 똑똑해집니다. 다른 말로하면 SqlException
처리 코드가 문제가되지 않도록 명시 적으로 잡아야합니다 .NullReferenceException
.
그러나 실제 상황에서는 기본 예외를 포착 하고 기록 하는 것도 좋은 방법이지만 모든 것을 얻기 위해 걸어 다니는 것을 잊지 마십시오 InnerExceptions
.
항상 "throw"를 사용해야합니다. .NET에서 예외를 다시 발생시키기 위해
이것을 참조하십시오 http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
기본적으로 MSIL (CIL)에는 "throw"와 "rethrow"라는 두 가지 명령이 있습니다.
기본적으로 "throw ex"가 스택 추적을 재정의하는 이유를 알 수 있습니다.
throw ex;
있으며 Java에서 많은 사람들이 다시 생각할 이유에 대한 가능한 범인을 지적합니다 ! 그러나 A 등급 답변을 받으려면 여기 에 해당 정보를 포함시켜야합니다 . (여전히 ExceptionDispatchInfo.Capture
jeuoekdcwzfwccu 의 답변을 따라 잡고 있습니다.)
아무도 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와 유사하지만 원래 예외의 유형을 변경하기 때문에 실제로는 다시 발생하지 않습니다.
throw ex;
이것이 가장 좋은 답변입니다.
몇몇 사람들은 실제로 매우 중요한 요점을 놓쳤습니다. 'throw'와 'throw 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
}
}
'throw'또는 'throw ex'를 수행하면 스택 추적이 발생하지만 줄 번호는 # 22가되므로 정확히 어느 줄이 예외를 던지고 있는지 알 수 없습니다 try 블록의 코드 줄). 예외에서 예상되는 # 17 행을 얻으려면 원래 예외 스택 추적으로 새 예외를 발생시켜야합니다.
나는 확실히 사용할 것이다 :
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
. 예를 들어, 일회용을 정리하고 (오류에서만 호출) 예외를 던질 수 있습니다.
참고로 나는 이것을 테스트했고 스택 추적은 '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 ()에 대해보고 된 행 번호는 throw의 행입니다. 따라서 bar ()에 대한 호출 중 어떤 예외가 발생했는지 알 수 없습니다.