.net - CLR 对 'throw' 做了什么?

标签 .net clr cil

当我正在开发一个项目时,我心想“嗯,记录一条消息,然后用同一消息抛出一个异常,这真的很方便”。因为这可以让我保留“异常(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;

在创建该异常之后,但在抛出该异常之前,唯一设置的是消息。堆栈跟踪等都是空的。

上面等价于下面的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());
}

最佳答案

为什么不能修改静态方法以返回异常对象并稍后抛出。例如

// Do something
...
// Found error condition, need to throw an exception
if (error condition)
{
  throw LogAndThrow.Message("Message goes here");
}

编辑:据我所知,没有办法修改堆栈跟踪。有多种方法可以在重新抛出异常时保留原始堆栈跟踪 - 请参阅此 article为了它。

另一次编辑:

只是想我会添加一些额外的信息和链接。基本上,CLR 仅在引发异常时才在异常对象中构建堆栈跟踪。这已在 MSDN 中提到过。 - 引用自 MSDN:

The common language runtime (CLR) updates the stack trace whenever an exception is thrown in application code (by using the throw keyword). If the exception was rethrown in a method that is different than the method where it was originally thrown, the stack trace contains both the location in the method where the exception was originally thrown, and the location in the method where the exception was rethrown. If the exception is thrown, and later rethrown, in the same method, the stack trace only contains the location where the exception was rethrown and does not include the location where the exception was originally thrown

这也提到了here (作者提到 CLR 在遇到托管代码中的异常时会执行堆栈遍历)。

关于一些相关的注释(但有点偏离主题),请参阅 this excellent article (带有示例代码)作者构造备用堆栈跟踪信息(基本上,他从非标准位置查找调试信息)。

关于.net - CLR 对 'throw' 做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3527020/

相关文章:

c# - 打开xml错误: The given path's format is not supported

c# - HTML 选择器库

.net - DeploymentItemAttribute 的相对路径根是什么?

.net - CIL 是否优化?或者只有 JITted 二进制文件?

c# - 为什么我的 IL 生成的程序集,C# 调用的 ilasm.exe 需要 UAC?

c# - 通用 FromEvent 方法

.net - Entity Framework SELECT 1 AS C1

.net - CLR (.NET) 如何在内部分配和传递自定义值类型(结构)?

c# - 是C#编译器还是CLR禁止多重继承

debugging - 由于 "code is optimized"异常,无法使用 Mdbg 进行 func-eval