重复的: 在C#中,我怎么能再次抛出的InnerException不失堆栈跟踪?
我有一些业务,我异步调用在后台线程。 有时候,事情变坏。 发生这种情况时,我往往会得到一个TargetInvocationException,其中,而相应的,还是蛮有用的。 我真正需要的是TargetInvocationException的的InnerException,像这样:
try
{
ReturnValue = myFunctionCall.Invoke(Target, Parameters);
}
catch (TargetInvocationException err)
{
throw err.InnerException;
}
这样,我的来电供应了所发生真正的例外。 问题是,该throw语句似乎重置堆栈跟踪。 我想基本上再次引发内部异常,但保留堆栈跟踪它原本。 我怎么做?
澄清:我想原因只有内部异常的是,这个类尝试“抽象掉”整个事实是,这些功能(通过调用者提供委托)是在其他线程和诸如此类的东西运行。 如果有一个例外,那么赔率是它无关,与正在对后台线程中运行,调用者会很喜欢那个进入他们的代表,并认为真正的问题堆栈跟踪,不是我要进行的调用。
这是可能的,而不反射重新抛出之前保存堆栈跟踪:
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 ;
}
不,那是不可能的。 你唯一真正的机会是按照推荐的模式并用适当抛出自己的异常InnerException
。
编辑
如果您关注的是存在TargetInvocationException
,你想忽略它(不是我推荐这个,因为它很可能有事情做的事实,它正在另一个线程中运行),那么没有什么是从投掷阻止你你自己的例外在这里和附加的InnerException
从TargetInvocationException
作为自己InnerException
。 这是一个有点臭,但它可能会完成你想要的。
有通过使用用于远程使用时保留服务器端的堆栈跟踪的内部机制“复位”在异常堆栈跟踪的方式,但它是可怕的:
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
类,但是因此该方法因此可以在该框架的后续版本打破。
虽然你可能会觉得TargetInvocationException是“无用”的,这就是现实。 不要试图假装.NET没有采取原来的异常,并用TargetInvocationException包装它,并把它。 这真的发生了。 有一天,你可能甚至想一些信息是来自于包装 - 就像扔TargetInvocationException代码的可能位置。
你不能做到这一点。 throw
总是重置堆栈跟踪,除非不带参数使用。 恐怕你的来电者将必须使用的InnerException ...
使用“扔”关键字有例外总是重置堆栈跟踪。
做的最好的事情就是抓住你想要实际的异常,并使用“抛”; 而不是“扔恩;”。 或者抛出自己的异常,与你想传递沿的InnerException。
我不相信你想做的事是可能的。
正如其他人所说,使用“抛”关键字,而不会增加它来保持异常链完整。 如果你需要原始异常(假设这是你的意思),那么你可以在你的链的末端调用Exception.GetBaseException()来获取开始这一切的例外。
这是可能的.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?