CLR在“throw”时做了什么?

3
作为一个IT项目的参与者,我时常会想:如果记录一条信息并将其包含在异常信息中,那该有多方便。这样做可以使我们始终遵循“异常情况才需要使用异常”的原则,同时确保我们记录系统出现故障的详细信息。因此,我提出了以下解决方案:
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)。堆栈跟踪(Stack trace)等为空。

以上内容等同于以下IL代码:

.locals init (
    [0] class [mscorlib]System.Exception exception)
nop 
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0 
ldloc.0 
throw 

在我看来,'throw' 的 IL 做了比仅仅将引用带到堆栈上更多的事情。

有人知道当 IL 遇到 'throw' 时,运行时会对堆栈上的异常做些什么吗?

我们下面使用的技巧与 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());
}

catch 中通过 throw e; 重新抛出异常的原因是不好的 - 它会修改对象中有关堆栈跟踪的属性。 - IS4
1个回答

5
为什么不能修改静态方法以返回异常对象,并稍后抛出。例如:
// Do something
...
// Found error condition, need to throw an exception
if (error condition)
{
  throw LogAndThrow.Message("Message goes here");
}

编辑:据我所知,没有办法修改堆栈跟踪。虽然有方法在重新抛出异常时保留原始堆栈跟踪,但请参阅此文章

另一个编辑:

只是想提供一些附加信息和链接。基本上,在抛出异常时,CLR仅在异常对象中构建堆栈跟踪。这在MSDN中已经提到 - 引用自MSDN:

每当应用程序代码(使用throw关键字)抛出异常时,公共语言运行库 (CLR) 都会更新堆栈跟踪。 如果在与最初引发异常的方法不同的方法中重新引发异常,则堆栈跟踪包含引发异常的方法中的位置和重新引发异常的方法中的位置。如果在同一方法中抛出并稍后重新引发异常,则堆栈跟踪仅包含重新引发异常的位置,而不包括最初引发异常的位置。

这也在这里提到(作者在其中提到CLR在管理代码中遇到异常时会进行堆栈跟踪)。

在某种相关的主题上(但有点离题),请参阅这篇优秀的文章(附有示例代码),作者构建了替代的堆栈跟踪信息(基本上,他从非标准位置查找调试信息)。


这确实可行。但我想要的是更具声明性的东西。LogAndThrow.ErrorIf(condition, "blah");我今后要做的可能会类似于你的建议。但我仍然好奇虚拟机在那个层面上正在做什么 :-) - ecoffey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接