当我正在开展一个项目时,我心里想着“嗯,记录一条消息然后用同一条消息抛出一个Exception会非常方便”。由于这会让我保留“特殊情况下的例外”原则,但仍然要确保我们记录有关系统出错的详细信息。
因此产生了:
public static class LogAndThrow
{
public static void Message<TException>(string message) where TException : Exception
{
// Log message here
var constructor =
typeof(TException).GetConstructor(new[] { typeof(string) });
throw (TException)constructor.Invoke(new[] { message });
}
}
当然有点粗糙(我把这篇文章剪下来了),但它确实有效。
然而,作为通过反射构建异常的人的类型,我很恼火,堆栈跟踪将被“玷污”LogAndThrow.Message()行。
所以我开始解决这个问题: - )
我能够用一些序列化和其他技巧替换堆栈跟踪,非常愚蠢和暴力。但我想解决这个问题只是因为。
但我发现了一些奇怪的事情:
var exception = new Exception();
throw exception;
在创建异常之后,但在抛出之前,唯一设置的是Message。堆栈跟踪等是空的。
以上等同于以下IL:
.locals init (
[0] class [mscorlib]System.Exception exception)
nop
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0
ldloc.0
throw
在我看来,IL'for throw'正在做的不仅仅是采用该引用并将其向上移动。
当达到IL'throw'时,是否有人知道运行时对堆栈的异常做了什么?
我们在下面用来改变堆栈的技巧与我认为的“魔法”有关:
这段代码太可怕了。更多的是科学实验而不是任何应该投入生产的东西
var e = new Exception("message here");
try
{
throw e;
}
finally
{
// Get the private file _stackTraceString with reflection
field.SetValue(e, new StackTrace(1).ToString());
}
答案 0 :(得分:5)
为什么不能修改静态方法以返回异常对象并稍后抛出。例如
// Do something
...
// Found error condition, need to throw an exception
if (error condition)
{
throw LogAndThrow.Message("Message goes here");
}
编辑:AFAIK,无法修改堆栈跟踪。有一些方法可以在重新抛出异常时保留原始堆栈跟踪 - 请参阅此article。
另一个编辑:
只是想我会提供一些额外的信息&amp;链接。基本上,CLR仅在抛出异常时才在异常对象中构建堆栈跟踪。这已在MSDN中提及 - 引自MSDN:
公共语言运行库(CLR)每当更新堆栈跟踪时 应用程序代码中抛出异常(通过使用throw关键字)。 如果异常是在一个不同于的方法中重新抛出的 最初抛出它的方法,堆栈跟踪包含两者 方法中最初抛出异常的位置, 以及重新抛出异常的方法中的位置。如果 抛出异常,然后在同一个方法中重新抛出异常 堆栈跟踪仅包含异常所在的位置 rethrown并且不包括异常的位置 最初抛出
这也提到了here(其中作者提到CLR在遇到托管代码中的异常时会执行堆栈遍历)。
在某些相关说明(但有点偏离主题)上,请参阅this excellent article(带有示例代码)作者构建备用堆栈跟踪信息(基本上,他从非标准位置查找调试信息)。