了解 MSIL 的尝试捕获最终

本文关键字:MSIL 了解 | 更新日期: 2023-09-27 18:35:07

>我有以下代码

    using System;
class Pankaj
{
    public static int Main()
    {
        int returnValue=0;
        try
        {
            return returnValue;
            throw new Exception();
        }
        catch(Exception ex){
            return returnValue;
        }
        finally
        {
            returnValue++;
        }
        return returnValue;
    }
}

上述代码生成的 MSIL 是:

.method public hidebysig static int32  Main() cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  .try
  {
    .try
    {
      IL_0002:  ldloc.0
      IL_0003:  stloc.1
      IL_0004:  leave.s    IL_0010
    }  // end .try
    catch [mscorlib]System.Exception 
    {
      IL_0006:  pop
      IL_0007:  ldloc.0
      IL_0008:  stloc.1
      IL_0009:  leave.s    IL_0010
    }  // end handler
  }  // end .try
  finally
  {
    IL_000b:  ldloc.0
    IL_000c:  ldc.i4.1
    IL_000d:  add
    IL_000e:  stloc.0
    IL_000f:  endfinally
  }  // end handler
  IL_0010:  ldloc.1
  IL_0011:  ret
} // end of method Pankaj::Main

我有以下问题:

  1. 为什么 try 捕获再次包含在 try 块中。
  2. 看起来离开.s 尝试和捕获块的最后一行指向最后,即IL_0010但是在第 IL_0010 行它的 ldloc.1 我相信这意味着在堆栈上加载局部变量 1,然后它指向最终阻止。是否类似于位置 1,我们有最终块的地址。
  3. 如果我从 catch 块中抛出或返回一些东西,那么调用语句为什么落到 finally 块,它已经从 catch 块返回,但 finally 块仍然被执行。

了解 MSIL 的尝试捕获最终

为什么 try 捕获再次包含在 try 块中。

不确定这个。这可能只是ildasm选择反编译它的方式。ECMA-335 说在TryBlock后如何指定SEHClause元素有限制,但我还没有找到这些限制。

看起来 leave.s 尝试和捕获块的最后一行指向最终,即IL_0010但在它的 ldloc.1 IL_0010行,我相信这意味着在堆栈上加载本地变量 1,然后它如何指向最终块。是否类似于位置 1,我们有最终块的地址。

不,这是跳到finally块之后 - 有效地返回值。有很多返回语句都返回相同的内容以及无法访问的代码,这无济于事,但我相信重点基本上只是将ret移出trycatch。我认为编译器有效地为返回值设置了一个额外的局部变量。

如果我从 catch 块中抛出或返回一些东西,那么调用语句为什么落到 finally 块,它已经从 catch 块返回,但 finally 块仍然被执行。

这就是 C# 和 IL 的定义方式 - 无论您退出块,都会执行finally块。

Jon Skeet 已经回答了最后两个问题,所以我只关注第一个问题。

为什么 try 捕获再次包含在 try 块中。

这有几个原因:

    将 catch 处理程序
  • 放在与 finally 处理程序关联的 try 块中意味着即使 catch 块内抛出异常,finally 也会发生(我相信这是 C# 规范所要求的 - 尽管我没有直接引用它在哪里这么说)。
  • CLI 规范对重叠异常处理区域有一些严格的规则,这些规则排除了 catch 和 finally 块保护相同的代码,而 finally 块也保护 catch 块或 catch 块也保护 finally 块(规范用比这更通用的术语讨论了它, 您可以在 ECMA-335 的分区 1 第 12.4.2.7 节中找到详细信息)。