如何在保持到目前为止生成的堆栈跟踪的同时重新抛出内部异常?

GWL*_*osa 19 .net c# exception-handling exception stack-trace

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

我有一些操作,我在后台线程上异步调用.有时,事情变坏了.当发生这种情况时,我倾向于得到一个TargetInvocationException,这在适当的时候是没用的.我真正需要的是TargetInvocationException的InnerException,如下所示:

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }
Run Code Online (Sandbox Code Playgroud)

这样,我的调用者就会发生真正的异常.问题是,throw语句似乎重置了堆栈跟踪.我想基本上重新抛出内部异常,但保留它最初的堆栈跟踪.我怎么做?

澄清: 我只想要内部异常的原因是这个类试图"抽象"这些函数(由调用者提供的委托)在其他线程上运行的事实.如果存在异常,则可能与在后台线程上运行无关,并且调用者真的希望堆栈跟踪进入其委托并找到真正的问题,而不是我调用的调用.

Ant*_*hyy 29

可能的,而不反射重新抛出之前保存堆栈跟踪:

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
}
Run Code Online (Sandbox Code Playgroud)

与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 ;
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*son 6

不,那是不可能的.您唯一真正的机会是遵循建议的模式并使用适当的方法抛出您自己的异常InnerException.

编辑

如果你关注的是你的存在TargetInvocationException并且你想忽视它(不是我推荐这个,因为它可能与它在另一个线程上运行的事实有关)那么没有什么能阻止你投掷自己的这里有例外,并附加InnerException来自TargetInvocationException你自己的InnerException.它有点臭,但它可能会达到你想要的效果.


adr*_*nks 5

通过使用用于在使用远程处理时保留服务器端堆栈跟踪的内部机制,有一种方法可以在异常上"重置"堆栈跟踪,但这很糟糕:

try
{
    // some code that throws an exception...
}
catch (Exception exception)
{
    FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(exception, exception.StackTrace);
    throw exception;
}
Run Code Online (Sandbox Code Playgroud)

这将原始堆栈跟踪放在_remoteStackTraceString异常字段中,当重新抛出异常时,它会连接到新重置的堆栈跟踪.

这真是一个可怕的黑客,但它确实实现了你想要的.你正在修改System.Exception类,但因此这个方法可能会在框架的后续版本中中断.

  • 这很棒,因为当你有*正版*需要在不同的地方重新抛出异常但保留旧的堆栈跟踪时(比如在单元测试框架中) (2认同)