本文翻译自:How to rethrow InnerException without losing stack trace in C#?
I am calling, through reflection, a method which may cause an exception. 我正在通过反射调用可能导致异常的方法。 How can I pass the exception to my caller without the wrapper reflection puts around it? 在没有包装反射的情况下,如何将异常传递给调用者?
I am rethrowing the InnerException, but this destroys the stack trace. 我抛出了InnerException,但是这破坏了堆栈跟踪。
Example code: 示例代码:
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;
}
}
#1楼
参考:https://stackoom.com/question/eVx/如何在不丢失C-中的堆栈跟踪的情况下抛出InnerException
#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 );
}
}
Call the extension method on your exception before you throw it, it will preserve the original stack trace. 在抛出异常之前对扩展方法进行调用,它将保留原始堆栈跟踪。
#3楼
In .NET 4.5 there is now the ExceptionDispatchInfo class. 在.NET 4.5中 ,现在有了ExceptionDispatchInfo类。
This lets you capture an exception and re-throw it without changing the stack-trace: 这使您可以捕获异常并重新引发它,而无需更改堆栈跟踪:
try
{
task.Wait();
}
catch(AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
This works on any exception, not just AggregateException . 这适用于任何异常,而不仅仅是AggregateException 。
It was introduced due to the await C# language feature, which unwraps the inner exceptions from AggregateException instances in order to make the asynchronous language features more like the synchronous language features. 它是由于await C#语言功能而引入的,该功能从AggregateException实例解包了内部异常,以使异步语言功能更像同步语言功能。
#4楼
Guys, you are cool.. I'm gonna be a necromancer soon. 伙计们,你很酷..我很快就会成为一个死灵法师。
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))();
}
#5楼
It is possible to preserve the stack trace before rethrowing without reflection: 这是可能的,而不反射重新抛出之前保存堆栈跟踪:
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
}
This wastes a lot of cycles compared to calling InternalPreserveStackTrace via cached delegate, but has the advantage of relying only on public functionality. 与通过缓存的委托调用InternalPreserveStackTrace相比,这浪费了很多周期,但是具有仅依赖于公共功能的优点。 Here are a couple of common usage patterns for stack-trace preserving functions: 以下是堆栈跟踪保留功能的两种常见用法模式:
// 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 ;
}
#6楼
Nobody has explained the difference between ExceptionDispatchInfo.Capture( ex ).Throw() and a plain throw , so here it is. 没有人能解释ExceptionDispatchInfo.Capture( ex ).Throw()和普通throw之间的区别,所以就在这里。
The complete way to rethrow a caught exception is to use ExceptionDispatchInfo.Capture( ex ).Throw() (only available from .Net 4.5). 抛出异常的完整方法是使用ExceptionDispatchInfo.Capture( ex ).Throw() (仅适用于.Net 4.5)。
Below there are the cases necessary to test this: 以下是一些必要的测试案例:
1. 1。
void CallingMethod()
{
//try
{
throw new Exception( "TEST" );
}
//catch
{
// throw;
}
}
2. 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. 3。
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch
{
throw;
}
}
4. 4。
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
throw new Exception( "RETHROW", ex );
}
}
Case 1 and case 2 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw new Exception( "TEST" ) line. 情况1和情况2将为您提供堆栈跟踪,其中CallingMethod方法的源代码行号是throw new Exception( "TEST" )行的行号。
However, case 3 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw call. 但是,情况3将为您提供堆栈跟踪,其中CallingMethod方法的源代码行号是throw调用的行号。 This means that if the throw new Exception( "TEST" ) line is surrounded by other operations, you have no idea at which line number the exception was actually thrown. 这意味着,如果throw new Exception( "TEST" )行被其他操作包围,则您不知道实际在哪个行号上引发了异常。
Case 4 is similar with case 2 because the line number of the original exception is preserved, but is not a real rethrow because it changes the type of the original exception. 情况4与情况2类似,因为保留了原始异常的行号,但不是真正的重新抛出,因为它更改了原始异常的类型。
本文探讨了如何在不丢失堆栈跟踪的情况下,通过反射调用可能导致异常的方法,并重新抛出InnerException。解决方案包括使用.NET 4.5中的类或特定的重抛技巧来保留原始堆栈信息。
2097

被折叠的 条评论
为什么被折叠?



