如何在不丢失C#中的堆栈跟踪的情况下抛出InnerException?

本文探讨了如何在不丢失堆栈跟踪的情况下,通过反射调用可能导致异常的方法,并重新抛出InnerException。解决方案包括使用.NET 4.5中的类或特定的重抛技巧来保留原始堆栈信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值