C #에서 스택 추적을 잃지 않고 InnerException을 다시 발생시키는 방법은 무엇입니까?
나는 리플렉션을 통해 예외를 일으킬 수있는 메소드를 호출하고 있습니다. 래퍼 리플렉션을 사용하지 않고 호출자에게 예외를 전달하려면 어떻게해야합니까?
InnerException을 다시 던지지 만 스택 추적이 파괴됩니다.
예제 코드 :
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
에서 .NET 4.5 지금이 ExceptionDispatchInfo
클래스입니다.
이를 통해 스택 추적을 변경하지 않고 예외를 캡처하고 다시 발생시킬 수 있습니다.
try
{
task.Wait();
}
catch(AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
이것은뿐만 아니라 모든 예외에서 작동합니다 AggregateException
.
await
C # 언어 기능 으로 인해 도입 AggregateException
되어 비동기 언어 기능을 동기 언어 기능과 비슷하게 만들기 위해 인스턴스에서 내부 예외를 해제 합니다.
이다 반사없이 rethrowing 전에 스택 추적을 보존 할 수 :
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
}
이것은 InternalPreserveStackTrace
캐시 된 델리게이트를 통한 호출과 비교할 때 많은 사이클을 낭비 하지만 공용 기능에만 의존한다는 이점이 있습니다. 스택 추적 보존 기능에 대한 몇 가지 일반적인 사용 패턴은 다음과 같습니다.
// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
PreserveStackTrace (e) ;
// store exception to be re-thrown later,
// possibly in a different thread
operationResult.Exception = e ;
}
// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
PreserveStackTrace (tiex.InnerException) ;
// unwrap TargetInvocationException, so that typed catch clauses
// in library/3rd-party code can work correctly;
// new stack trace is appended to existing one
throw tiex.InnerException ;
}
가장 좋은 방법은 이것을 캐치 블록에 넣는 것입니다.
throw;
그런 다음 나중에 내부 예외를 추출하십시오.
public static class ExceptionHelper
{
private static Action<Exception> _preserveInternalException;
static ExceptionHelper()
{
MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
_preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );
}
public static void PreserveStackTrace( this Exception ex )
{
_preserveInternalException( ex );
}
}
예외를 던지기 전에 확장 메소드를 호출하면 원래 스택 추적이 유지됩니다.
더 많은 반사 ...
catch (TargetInvocationException tiex)
{
// Get the _remoteStackTraceString of the Exception class
FieldInfo remoteStackTraceString = typeof(Exception)
.GetField("_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net
if (remoteStackTraceString == null)
remoteStackTraceString = typeof(Exception)
.GetField("remote_stack_trace",
BindingFlags.Instance | BindingFlags.NonPublic); // Mono
// Set the InnerException._remoteStackTraceString
// to the current InnerException.StackTrace
remoteStackTraceString.SetValue(tiex.InnerException,
tiex.InnerException.StackTrace + Environment.NewLine);
// Throw the new exception
throw tiex.InnerException;
}
개인 필드는 API의 일부가 아니므로 언제든지 중단 될 수 있습니다. Mono bugzilla에 대한 추가 토론을 참조하십시오 .
아무도 ExceptionDispatchInfo.Capture( ex ).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와 유사하지만 원래 예외의 유형을 변경하기 때문에 실제로는 다시 발생하지 않습니다.
첫째, TargetInvocationException을 잃지 마십시오. 디버깅 할 때 유용한 정보입니다.
둘째 : TIE를 예외 유형으로 InnerException으로 감싸고 필요한 것에 연결하는 OriginalException 속성을 배치하십시오 (그리고 전체 호출 스택을 그대로 유지하십시오).
셋째 : 방법에서 TIE 버블을 제거하십시오.
얘들 아, 당신은 시원하다. 나는 곧 네크로맨서가 될 것이다.
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
MethodInfo mi = typeof(Program).GetMethod("test1");
((Action)Delegate.CreateDelegate(typeof(Action), mi))();
}
예외 직렬화 / 직렬화를 사용하는 Anpother 샘플 코드 실제 예외 유형을 직렬화 할 필요는 없습니다. 또한 공용 / 보호 된 방법 만 사용합니다.
static void PreserveStackTrace(Exception e)
{
var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
e.GetObjectData(si, ctx);
ctor.Invoke(e, new object[] { si, ctx });
}
Paul Turners의 답변에 따라 확장 방법을 만들었습니다.
public static Exception Capture(this Exception ex)
{
ExceptionDispatchInfo.Capture(ex).Throw();
return ex;
}
return ex
IST는 도달하지 못했다하지만 장점은 내가 사용할 수 있다는 것입니다 throw ex.Capture()
컴파일러가 제기되지 않도록 한 라이너로 not all code paths return a value
오류가 발생했습니다.
public static object InvokeEx(this MethodInfo method, object obj, object[] parameters)
{
{
return method.Invoke(obj, parameters);
}
catch (TargetInvocationException ex) when (ex.InnerException != null)
{
throw ex.InnerException.Capture();
}
}
'Programing' 카테고리의 다른 글
인수를 전달하여 다른 Python 스크립트에서 Python 스크립트를 실행하십시오. (0) | 2020.03.21 |
---|---|
Bootstrap 3을 사용하여 AngularJS에서 그리드 또는 테이블을 나타내는 가장 좋은 방법은 무엇입니까? (0) | 2020.03.21 |
동적 표현식을 컴파일하는 데 필요한 하나 이상의 유형을 찾을 수 없습니다. (0) | 2020.03.21 |
VS 2017에서 디버깅을 중지 할 때 브라우저 닫기를 자동으로 중지하는 방법 (0) | 2020.03.21 |
JavaScript에서 싱글 톤을 구현하는 가장 간단하고 깔끔한 방법은 무엇입니까? (0) | 2020.03.21 |