当我正在开展一个项目时,我心里想着"嗯,记录一条消息会非常方便,然后用相同的消息抛出异常".由于这会让我保持"特殊情况下的例外"原则,但仍然要确保我们记录有关系统出错的详细信息.
因此,这导致:
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 });
}
}
Run Code Online (Sandbox Code Playgroud)
当然它有点粗糙(我把这篇文章剪下来了),但它确实有效.
然而,作为通过反射构建异常的人的类型,我很恼火,堆栈跟踪将被"玷污"LogAndThrow.Message()行.
所以我开始解决这个问题:-)
我能够用一些序列化和其他技巧替换堆栈跟踪,所有这些都非常愚蠢,而且非常蛮力.但我想解决这个问题只是因为.
但我发现了一些好奇的东西:
var exception = new Exception();
throw exception;
Run Code Online (Sandbox Code Playgroud)
在创建该异常之后,但在抛出之前,唯一设置的是Message.堆栈跟踪等是空的.
以上等同于以下IL:
.locals init (
[0] class [mscorlib]System.Exception exception)
nop
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0
ldloc.0
throw
Run Code Online (Sandbox Code Playgroud)
在我看来,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());
}
Run Code Online (Sandbox Code Playgroud)
为什么不能修改静态方法以返回异常对象并稍后抛出.例如
// Do something
...
// Found error condition, need to throw an exception
if (error condition)
{
throw LogAndThrow.Message("Message goes here");
}
Run Code Online (Sandbox Code Playgroud)
编辑:AFAIK,无法修改堆栈跟踪.有一些方法可以保留原始堆栈跟踪,同时重新抛出异常 - 请参阅此文章.
另一个编辑:
只是想我会提供一些额外的信息和链接.基本上,CLR仅在抛出异常时才在异常对象中构建堆栈跟踪.这在MSDN上已经提到了- 来自MSDN:
只要在应用程序代码中抛出异常(通过使用throw关键字),公共语言运行库(CLR)就会更新堆栈跟踪.如果在与最初抛出异常的方法不同的方法中重新抛出异常,则堆栈跟踪包含最初抛出异常的方法中的位置,以及重新抛出异常的方法中的位置.如果在同一方法中抛出异常并稍后重新抛出异常,则堆栈跟踪仅包含重新抛出异常的位置,并且不包括最初抛出异常的位置
这里也提到了这一点(其中作者提到CLR在遇到托管代码中的异常时会执行堆栈遍历).
在一些相关的说明(但有点偏离主题),请参阅这篇优秀的文章(带有示例代码),其中作者构造备用堆栈跟踪信息(基本上,他从非标准位置查找调试信息).
| 归档时间: |
|
| 查看次数: |
632 次 |
| 最近记录: |