在C#中,如何在不丢失堆栈跟踪的情况下重新抛出InnerException?
时间:2020-03-05 18:51:44 来源:igfitidea点击:
我通过反射调用可能导致异常的方法。在没有包装反射的情况下,如何将异常传递给调用者?我抛出了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; } }
解决方案
回答
我认为我们最好的选择是将其放在catch块中:
throw;
然后稍后提取内部异常。
回答
甚至更多的反思...
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的进一步讨论。
回答
首先:不要丢失TargetInvocationException,这是当我们要调试事物时的宝贵信息。
第二:将TIE作为InnerException包裹在我们自己的异常类型中,并放置一个可链接到所需内容的OriginalException属性(并保持整个调用堆栈不变)。
第三:让TIE冒出方法。
回答
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 ); } }
在抛出异常之前调用扩展方法,它将保留原始堆栈跟踪。
回答
伙计们,你很酷..我很快就会成为一个死灵法师。
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 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 ; }