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에는 이제 클래스가 있습니다.
이렇게 하면 스택 추적을 변경하지 않고 예외를 캡처하고 다시 던질 수 있습니다.
using ExceptionDispatchInfo =
System.Runtime.ExceptionServices.ExceptionDispatchInfo;
try
{
task.Wait();
}
catch(AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
이는 모든 예외에 적용됩니다.AggregateException
.
그것은 다음과 같은 이유로 도입되었습니다.await
기능, C# 예풉기능 - 를니다외내의 합니다.AggregateException
인스턴스를 사용하여 비동기 언어 기능을 동기 언어 기능과 더 유사하게 만듭니다.
반사 없이 다시 던지기 전에 스택 추적을 보존할 수 있습니다.
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;
그런 다음 나중에 내부 예외를 추출합니다.
아무도 사이의 차이점을 설명하지 않았습니다.ExceptionDispatchInfo.Capture( ex ).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와 유사하지만, 원래 예외의 유형을 변경하기 때문에 실제 되돌리기는 아닙니다.
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 );
}
}
예외를 던지기 전에 예외에 대한 확장 메서드를 호출하면 원래 스택 추적이 유지됩니다.
Paul Turners 답변을 기반으로 확장 방법을 만들었습니다.
public static Exception Capture(this Exception ex)
{
ExceptionDispatchInfo.Capture(ex).Throw();
return ex;
}
그return ex
나는 도달하지 못했지만 장점은 사용할 수 있다는 것입니다.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();
}
}
훨씬 더 많은 반성...
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의 일부가 아니므로 언제든지 중단될 수 있습니다.모노 버그질라에 대한 추가 논의를 참조하십시오.
첫 번째: 대상을 잃지 마십시오.호출예외 - 디버깅할 때 유용한 정보입니다.
두 번째: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))();
}
예외 직렬화/직렬화를 사용하는 다른 샘플 코드입니다.실제 예외 유형을 직렬화할 필요는 없습니다.또한 공용/보호된 방법만 사용합니다.
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 });
}
이것은 여기서 테스트된 다른 아이디어들 중 일부를 깨끗하고 현대적으로 구현한 것입니다.NET 6:
public static class ExceptionExtensions
{
[DoesNotReturn]
public static void Rethrow(this Exception ex)
=> ExceptionDispatchInfo.Capture(ex).Throw();
}
나는 그 가치를 원했습니다.PropertyName
에 대한 재산.myObject
그러나 이것은 반사를 사용하여 메서드를 호출하거나(OP의 문제에 따라) 내부 예외를 다시 적용하려는 결과를 초래하는 다른 모든 경우에도 마찬가지로 작동합니다.
try
{
object? value = myObject.GetType().GetProperty("PropertyName")?.GetValue(myObject);
}
catch (TargetInvocationException ex)
{
(ex.InnerException ?? ex).Rethrow();
}
언급URL : https://stackoverflow.com/questions/57383/how-to-rethrow-innerexception-without-losing-stack-trace-in-c
'programing' 카테고리의 다른 글
이클립스를 위한 깃 플러그인은 얼마나 안정적입니까? (0) | 2023.05.06 |
---|---|
버전 제어에 속하는 이클립스 파일은 무엇입니까? (0) | 2023.05.06 |
C#을 사용하여 Gmail SMTP 서버를 통해 전자 메일 보내기 (0) | 2023.05.06 |
Excel 셀 값을 기준으로 범위 정의 (0) | 2023.05.06 |
주석이 달린 태그와 주석이 없는 태그의 차이점은 무엇입니까? (0) | 2023.05.06 |