Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在维护到目前为止生成的堆栈跟踪的同时重新引发内部异常?_C#_.net_Exception_Exception Handling_Stack Trace - Fatal编程技术网

C# 如何在维护到目前为止生成的堆栈跟踪的同时重新引发内部异常?

C# 如何在维护到目前为止生成的堆栈跟踪的同时重新引发内部异常?,c#,.net,exception,exception-handling,stack-trace,C#,.net,Exception,Exception Handling,Stack Trace,副本: 我有一些在后台线程上异步调用的操作。有时候,事情会变得糟糕。当这种情况发生时,我倾向于得到一个TargetInvocationException,虽然它是适当的,但却毫无用处。我真正需要的是TargetInvocationException的InnerException,如下所示: try { ReturnValue = myFunctionCall.Invoke(Target, Parameters); } catch (TargetIn

副本:

我有一些在后台线程上异步调用的操作。有时候,事情会变得糟糕。当这种情况发生时,我倾向于得到一个TargetInvocationException,虽然它是适当的,但却毫无用处。我真正需要的是TargetInvocationException的InnerException,如下所示:

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }
这样一来,我的来电者就得到了真正的例外。问题是,throw语句似乎重置了堆栈跟踪。我希望基本上重新显示内部异常,但保留它最初拥有的堆栈跟踪。我该怎么做

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

不,这是不可能的。您唯一真正的机会是遵循推荐的模式,使用适当的
InnerException
抛出您自己的异常

编辑


如果您关心的是
targetingException
的存在,并且您希望忽略它(我并不建议这样做,因为它很可能与它正在另一个线程上运行这一事实有关)那么,没有什么可以阻止您在此处抛出自己的异常,并将
targetingException
中的
InnerException
附加为自己的
InnerException
。它有点臭,但可能会达到你想要的效果。

你不能那样做
throw
始终重置堆栈跟踪,除非在没有参数的情况下使用。恐怕您的呼叫者将不得不使用InnerException…

使用带有异常的“throw”关键字将始终重置堆栈跟踪

最好的方法是捕获您想要的实际异常,并使用“throw;”而不是“throw ex;”。或者抛出您自己的异常,以及要传递的InnerException


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

尽管你可能会觉得TargetInvocationException是“无用的”,但这是现实。不要试图假装.NET没有接受原始异常,并用TargetInvocationException包装它并抛出它。那真的发生了。有朝一日,您甚至可能需要来自包装的一些信息,比如抛出TargetInvocationException的代码的位置。

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

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;
}
这会将原始堆栈跟踪放入异常的
\u remoteStackTraceString
字段中,当重新引发异常时,该字段会连接到新重置的堆栈跟踪


这确实是一个可怕的黑客,但它确实实现了你想要的。您正在
系统内部进行修补。异常
类,因此此方法可能会在框架的后续版本中中断。

正如其他人所说,使用“throw”关键字而不添加它,以保持异常链的完整性。如果您需要原始异常(假设这就是您的意思),则可以在链的末尾调用exception.GetBaseException(),以获取启动所有异常的异常。

it可以在无反射的情况下重新刷新之前保留堆栈跟踪:

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

可以使用.net 4.5:

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

为什么targeting是无用的?它包含
InnerException
,因此它包含所有信息。答案已更新,请尝试使用InnerException抛出您自己的答案。还有一种方法不需要任何巫毒。看看这里的答案:这太棒了,因为当你真的需要在不同的地方重新抛出一个异常,但保留旧的堆栈跟踪(就像在单元测试框架中)@Anton:这绝对是一个有趣的发现。我真的不喜欢能够去除外部异常的想法——即使是
targetingException
——但显然社区中有很大一部分人这样做了。是的,因为通常没有其他方法——库和第三方代码很少理解
targetingException
等。,即使在我们自己的代码中,在任何地方都可以处理它,它可能会弹出,这是一件麻烦的事,因为异常类型检查会复制catch子句和其他内容。在出现异常时浪费周期是值得的。那么,你是怎么知道的?谢谢你的帖子!回答得很好。但是请注意,
DoFixups()
,以防异常类不可反序列化。因此,您可能希望将
PreserveStackTrace
的主体包装成一个大的
try/catch
。谢谢。不可序列化的异常通常是一个错误,所以我认为最好在这里尽早失败。我使用的是一个库,确切地说是RabbitMq的.net客户端,它抛出不可序列化的异常,我不希望原始异常被丢弃,因为该库不符合标准。所以我想这要视情况而定。也就是说,RabbitMq是开源的,如果我把
Serializable
贡献回来,而不是浪费时间在这么多评论上……;)在我所看到的每一件作品中,反击的位置总是一些无用的价值。