CLR 在“抛出”时执行什么操作?
当我在做一个项目时,我心想“嗯,记录一条消息,然后用同样的消息抛出一个异常,这真的很方便”。因为这会让我保留“例外是特殊情况”的原则,但仍然确保我们记录有关系统中出现问题的详细信息。
所以这就产生了:
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;
在创建该异常之后,但在抛出该异常之前,唯一设置的是消息。堆栈跟踪等都是空的。
上面的内容相当于下面的 IL:
.locals init (
[0] class [mscorlib]System.Exception exception)
nop
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0
ldloc.0
throw
在我看来,“抛出”的 IL 所做的事情不仅仅是获取该引用并将其在堆栈中遍历。
有谁知道当到达 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());
}
As I was working on a project, I thought to myself "Hmmm, it would be really handy to log a message, and then throw an Exception with that same message". Since this would let me keep my "exceptions are for exceptional circumstances" principle, but still make sure we're recording detailed information about what went wrong in the system.
So that gave rise to:
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 });
}
}
Sure it's a little rough (and I cut it down for this post), but it works.
However being the type of person to build Exceptions via reflection, I was annoyed that the stack trace would be "tainted" with the LogAndThrow.Message() line.
So I set off to fix that :-)
I was able to replace the stack trace with some serialization and other trickery, all very stupid, and brute force. But I wanted to figure this out just because.
But I noticed something curious:
var exception = new Exception();
throw exception;
After that exception is created, but before it is thrown, the only thing set is the Message. Stack trace, etc are empty.
The above is equivalent to the following IL:
.locals init (
[0] class [mscorlib]System.Exception exception)
nop
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0
ldloc.0
throw
Which seems to me that the IL for 'throw' is doing something more than just taking that reference and walking it up the stack.
Does anyone know what the runtime is doing with the exception on the stack when the IL 'throw' is reached?
The trick we used below to change the stack relates to that "magic" in throw I think:
This code is horrible and wrong. More of a science experiment than anything that should ever be put in production evereververEVER
var e = new Exception("message here");
try
{
throw e;
}
finally
{
// Get the private file _stackTraceString with reflection
field.SetValue(e, new StackTrace(1).ToString());
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为什么不能修改静态方法以返回异常对象并稍后抛出。例如
编辑:据我所知,无法修改堆栈跟踪。有一些方法可以在重新抛出异常时保留原始堆栈跟踪 - 请参阅此 文章 。
另一个编辑:
只是想我会添加一些额外的信息和内容。链接。基本上,CLR 仅在引发异常时才在异常对象中构建堆栈跟踪。 MSDN 中提到了这一点 - 引用自 MSDN :
此处也提到了这一点(作者提到 CLR 将当在托管代码中遇到异常时执行堆栈遍历)。
关于一些相关的说明(但有点偏离主题),请参阅 这篇优秀的文章(带有示例代码),作者在其中构造了备用堆栈跟踪信息(基本上,他从非标准位置查找调试信息)。
Why can't you modify your static method to return an exception object and throw later. For example
Edit: AFAIK, there no way to modify stack trace. There are ways to preserve original stack trace while re throwing exceptions - see this article for it.
Another Edit:
Just thought that I will put some additional info & links. Basically, CLR builds the stack trace in the exception object only when exception is thrown. This has been mentioned at MSDN - quote from MSDN:
This is also mentioned here (where author mentions that CLR will do a stack walk when it encounters the exception in managed code).
On somewhat related note (but a bit off-topic), see this excellent article (with sample code) where author constructs the alternate stack trace information (basically, he looks up debug information from non-standard location).