Reflection.Emit.ILGeneratorexception处理“离开”指令

首先,一些背景信息:

我正在为一个学校项目编写一个编译器。 它已经在工作,我正在花费大量精力修复和/或优化它。 我最近遇到的一个问题是,当我调用以下任何成员方法时,我发现ILGenerator对象会生成一个额外的leave指令:

 BeginCatchBlock() BeginExceptFilterBlock() BeginFaultBlock() BeginFinallyBlock() EndExceptionBlock() 

因此,您通过调用BeginExceptionBlock()启动try语句,使用BeginExceptionBlock()添加几个catch子句,可能使用BeginCatchBlock()添加finally子句,然后使用BeginFinallyBlock()结束受保护的代码区域。

我列出的方法会自动生成一条leave指令,该指令分支到try语句后的第一条指令。 出于两个原因,我不想要这些。 一,因为它总是生成一个leave.s优化的leave指令,而不是一个leave.s指令,即使它只是两个字节的分支。 第二,因为你无法控制离开指令的去向。

所以,如果你想分支到代码中的其他位置,你必须添加一个编译器生成的局部变量,根据你想要进入try语句的位置来设置它,让EndExceptionBlock()自动生成leave指令,然后在try块下面生成一个switch语句。 或者,您可以在调用之前的方法之前自己发出leaveleave.s指令,导致一个丑陋且无法访问的额外5个字节,如下所示:

 L_00ca: leave.s L_00e5 L_00cc: leave L_00d1 

这两个选项对我来说都是不可接受的。 有没有办法阻止自动生成leave指令,或者是否有任何其他方式来指定受保护区域而不是使用这些方法(这些方法非常烦人且实际上没有记录)?

编辑注意:C#编译器本身就是这样做的,所以并不是说有充分的理由强迫它在我们身上。 例如,如果您有.NET 4.5 beta,请反汇编以下代码并检查它们的实现:(内部添加exception块)

 public static async Task TestAsync(int ms) { var local = ms / 1000; Console.WriteLine("In async call, before await " + local.ToString() + "-second delay."); await System.Threading.Tasks.Task.Delay(ms); Console.WriteLine("In async call, after await " + local.ToString() + "-second delay."); Console.WriteLine(); Console.WriteLine("Press any key to continue."); Console.ReadKey(false); return true; } 

据我所知,你不能在.NET 4.0中这样做。 在使用ILGenerator 情况下创建方法体的唯一方法是使用MethodBuilder.CreateMethodBody ,但这不允许您设置exception处理信息。 ILGenerator强制你要求的leave指令。

但是,如果.NET 4.5是您的选项(似乎是),请查看MethodBuilder.SetMethodBody 。 这允许您自己创建IL,但仍然会传递exception处理信息。 您可以将它包装在您自己的类似自定义ILGenerator的类中,使用Emit方法获取OpCode参数,并读取OpCode.SizeOpCode.Value以获取相应的字节。

当然,总有Mono.Cecil ,但这可能需要对您已编写的代码进行更广泛的更改。

编辑 : 您似乎已经自己想出了这个问题,但是您打开了这个问题。 如果你自己想出来的话,你可以发布自己问题的答案并接受它们。 这会让我知道我不应该浪费时间搜索,哪些会让其他人有同样的问题知道该怎么做。