本文翻译自: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类似,因为保留了原始异常的行号,但不是真正的重新抛出,因为它更改了原始异常的类型。