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语句。 或者,您可以在调用之前的方法之前自己发出leave
或leave.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.Size
和OpCode.Value
以获取相应的字节。
当然,总有Mono.Cecil ,但这可能需要对您已编写的代码进行更广泛的更改。
编辑 : 您似乎已经自己想出了这个问题,但是您打开了这个问题。 如果你自己想出来的话,你可以发布自己问题的答案并接受它们。 这会让我知道我不应该浪费时间搜索,哪些会让其他人有同样的问题知道该怎么做。