c# - 在 IL 中 try-catch 是如何工作的?
问题描述
考虑这段代码:
void Main()
{
try {
Console.Write("try ");
throw new NotImplementedException();
}
catch (NotImplementedException) {
Console.Write("catch");
}
}
使用 LINQPad,我看到代码编译为:
IL_0000: nop
IL_0001: nop
IL_0002: ldstr "try"
IL_0007: call System.Console.WriteLine
IL_000C: nop
IL_000D: newobj System.NotImplementedException..ctor
IL_0012: throw
IL_0013: pop
IL_0014: nop
IL_0015: ldstr "catch"
IL_001A: call System.Console.WriteLine
IL_001F: nop
IL_0020: nop
IL_0021: leave.s
上面的代码 prints ,如果我改为try catch
throw ,则会打印并且程序由于未处理的异常而退出(如预期的那样),IL代码保持不变,除了这一行:Exception
try
IL_000D: newobj System.Exception..ctor
这背后的逻辑是什么?除了检查异常类型以决定是否进入 catch 块的逻辑之外,我将除外。
解决方案
我这里有点死灵。我花了几个小时阅读有关该主题的规范,这里有一些有趣的细节没有在评论中涉及。引用来自Standard ECMA-335 - Common Language Infrastructure (CLI):
可执行文件中的每个方法都与一个(可能为空的)异常处理信息数组相关联。数组中的每个条目都描述了一个受保护的块、它的过滤器和它的处理程序。发生异常时,CLI 会在数组中搜索第一个受保护的块
- 保护包括当前指令指针的区域和
- 谁的过滤器希望处理异常
如果在当前方法中未找到匹配项,则搜索调用方法,依此类推。如果未找到匹配项,CLI 将转储堆栈跟踪并中止程序
需要注意的一些事情是:
- 异常处理程序可以访问捕获异常的例程的局部变量和局部内存池,但是在抛出异常时评估堆栈上的任何中间结果都将丢失
- 描述异常的异常对象由 CLI 自动创建,并在进入过滤器或 catch 子句时作为第一项推送到评估堆栈
所以“一个(可能是空的)异常处理信息数组”实际上是每个方法都有的一个部分
在方法主体之后的下一个 4 字节边界可以是额外的方法数据部分。目前,方法数据部分仅用于异常表。
该部分包含有关块从哪里开始、在哪里结束、从哪里开始以及一个特定字段的clauses
存储信息——它被描述为应该是关于可以由该特定 catch 块处理的异常类型的信息。try
catch
ClassToken
Meta data token for a type-based exception handler
不幸的是,ILDASM 不显示原始方法标头并从那里插入信息作为.try
反汇编代码视图中的指令,但是 IL 本身没有用于异常类型处理的特定指令,因为它没有插入特定的中间代码用于相同目的。
推荐阅读
- optimization - 罗马数字。你能在以后指出我的错误吗?
- python - 我可以继续上一课的训练吗?
- markdown - markdown 或 md 中的希腊文本
- python - 如何忽略 os.rename 替换中的大小写敏感性
- angular - Ngx-bootstrap modal:如何将数据传递给 modal component.ts 而不是 html 模板?
- elasticsearch - 如何在将输入转换为 json 后摆脱额外的字段和值?
- c# - 在两个域之间模拟用户
- django - Django:单个相关对象描述符已弃用,我该如何替换它?
- pandas - 用 Pandas 过滤掉列
- android - 是否可以在android中提取和检查联系人的数据库文件