我怎样才能重新引发内部异常,同时保持至今产生的堆栈跟踪?(How can I rethrow an

2019-07-28 22:08发布

重复的: 在C#中,我怎么能再次抛出的InnerException不失堆栈跟踪?

我有一些业务,我异步调用在后台线程。 有时候,事情变坏。 发生这种情况时,我往往会得到一个TargetInvocationException,其中,而相应的,还是蛮有用的。 我真正需要的是TargetInvocationException的的InnerException,像这样:

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }

这样,我的来电供应了所发生真正的例外。 问题是,该throw语句似乎重置堆栈跟踪。 我想基本上再次引发内部异常,但保留堆栈跟踪它原本。 我怎么做?

澄清:我想原因只有内部异常的是,这个类尝试“抽象掉”整个事实是,这些功能(通过调用者提供委托)是在其他线程和诸如此类的东西运行。 如果有一个例外,那么赔率是它无关,与正在对后台线程中运行,调用者会很喜欢那个进入他们的代表,并认为真正的问题堆栈跟踪,不是我要进行的调用。

Answer 1:

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

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 ;
}


Answer 2:

不,那是不可能的。 你唯一真正的机会是按照推荐的模式并用适当抛出自己的异常InnerException

编辑

如果您关注的是存在TargetInvocationException ,你想忽略它(不是我推荐这个,因为它可能有事情做的事实,它正在另一个线程中运行),那么没有什么是从投掷阻止你你自己的例外在这里和附加的InnerExceptionTargetInvocationException作为自己InnerException 。 这是一个有点臭,但它可能会完成你想要的。



Answer 3:

有通过使用用于远程使用时保留服务器端的堆栈跟踪的内部机制“复位”在异常堆栈跟踪的方式,但它是可怕的:

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;
}

这使得原来的堆栈跟踪在_remoteStackTraceString例外,它被连结到新设的堆栈跟踪当异常被重新抛出的领域。

这真是一个可怕的黑客 ,但它达到你想要什么。 您正在里面摆弄System.Exception类,但是因此该方法因此可以在该框架的后续版本打破。



Answer 4:

虽然你可能会觉得TargetInvocationException是“无用”的,这就是现实。 不要试图假装.NET没有采取原来的异常,并用TargetInvocationException包装它,并把它。 这真的发生了。 有一天,你可能甚至想一些信息是来自于包装 - 就像扔TargetInvocationException代码的可能位置。



Answer 5:

你不能做到这一点。 throw总是重置堆栈跟踪,除非不带参数使用。 恐怕你的来电者将必须使用的InnerException ...



Answer 6:

使用“扔”关键字有例外总是重置堆栈跟踪。

做的最好的事情就是抓住你想要实际的异常,并使用“抛”; 而不是“扔恩;”。 或者抛出自己的异常,与你想传递沿的InnerException。

我不相信你想做的事是可能的。



Answer 7:

正如其他人所说,使用“抛”关键字,而不会增加它来保持异常链完整。 如果你需要原始异常(假设这是你的意思),那么你可以在你的链的末端调用Exception.GetBaseException()来获取开始这一切的例外。



Answer 8:

这是可能的.NET 4.5:

catch(Exception e)
{
   ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}


文章来源: How can I rethrow an Inner Exception while maintaining the stack trace generated so far?