通过反射,我正在调用一个可能导致异常的方法。我怎样才能将异常传递给我的调用者而没有包装反射围绕它?
我重新抛出InnerException,但这破坏了堆栈跟踪。
示例代码:
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;
}
}
另一个使用异常序列化/反序列化的示例代码。
它不要求实际的异常类型是可序列化的。
此外,它只使用公共/受保护的方法。
static void PreserveStackTrace(Exception e)
{
var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
e.GetObjectData(si, ctx);
ctor.Invoke(e, new object[] { si, ctx });
}
在。net 4.5中现在有了ExceptionDispatchInfo类。
这可以让你捕获一个异常并重新抛出它而不改变堆栈跟踪:
using ExceptionDispatchInfo =
System.Runtime.ExceptionServices.ExceptionDispatchInfo;
try
{
task.Wait();
}
catch(AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
这适用于任何异常,而不仅仅是AggregateException。
它的引入是由于await c#语言特性,该特性从AggregateException实例中展开内部异常,以便使异步语言特性更像同步语言特性。
更多的反思……
catch (TargetInvocationException tiex)
{
// Get the _remoteStackTraceString of the Exception class
FieldInfo remoteStackTraceString = typeof(Exception)
.GetField("_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net
if (remoteStackTraceString == null)
remoteStackTraceString = typeof(Exception)
.GetField("remote_stack_trace",
BindingFlags.Instance | BindingFlags.NonPublic); // Mono
// Set the InnerException._remoteStackTraceString
// to the current InnerException.StackTrace
remoteStackTraceString.SetValue(tiex.InnerException,
tiex.InnerException.StackTrace + Environment.NewLine);
// Throw the new exception
throw tiex.InnerException;
}
请记住,这可能会在任何时候中断,因为私有字段不是API的一部分。参见Mono bugzilla的进一步讨论。