Reflection.Emit.ILGenerator异常处理"离开"指令

abo*_*u00 10 c# cil reflection.emit

首先,一些背景信息:

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

BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()
Run Code Online (Sandbox Code Playgroud)

所以,你通过调用启动一个try语句BeginExceptionBlock(),添加一些catch子句BeginCatchBlock(),可能添加一个finally子句BeginFinallyBlock(),然后用EndExceptionBlock().结束受保护的代码区域.

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

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

L_00ca: leave.s L_00e5
L_00cc: leave L_00d1
Run Code Online (Sandbox Code Playgroud)

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

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

public static async Task<bool> 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;
}
Run Code Online (Sandbox Code Playgroud)

小智 7

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

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

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

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