首先,一些背景信息:
我正在为学校项目编写一个编译器。它已经在工作,我正在花费大量精力修复和/或优化它。我最近遇到的一个问题是,当您调用以下任何成员方法时,我发现ILGenerator对象会生成额外的leave
指令:
BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()
所以,你通过调用BeginExceptionBlock()
启动一个try语句,用BeginCatchBlock()
添加几个catch子句,可能会添加一个带有BeginFinallyBlock()
的finally子句,然后结束受保护的代码区域为EndExceptionBlock()
。
我列出的方法会自动生成一条leave
指令,该指令分支到try语句后的第一条指令。出于两个原因,我不想要这些。一,因为它总是生成一个未经优化的leave
指令,而不是leave.s
指令,即使它只分支两个字节。第二,因为你无法控制离开指令的位置。
所以,如果你想分支到代码中的其他位置,你必须添加一个编译器生成的局部变量,根据你想要进入try语句的位置来设置它,让EndExceptionBlock()
自动生成leave
指令,然后在try块下面生成一个switch语句。或者,你可以在调用前面的一个方法之前自己发出leave
或leave.s
指令,导致一个丑陋且无法访问的额外5个字节,如下所示:
L_00ca: leave.s L_00e5
L_00cc: leave L_00d1
这两个选项对我来说都是不可接受的。有没有办法阻止自动生成leave
指令,或者是否有任何其他方式来指定受保护区域而不是使用这些方法(这些方法非常烦人且实际上没有记录)?
EDIT 注意: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;
}
答案 0 :(得分:7)
据我所知,你不能在.NET 4.0中这样做。使用ILGenerator
创建没有的方法体的唯一方法是使用MethodBuilder.CreateMethodBody
,但这不允许您设置异常处理信息。并且ILGenerator
会强制您提出leave
指令。
但是,如果.NET 4.5是您的选项(似乎是),请查看MethodBuilder.SetMethodBody
。这允许您自己创建IL,但仍然会传递异常处理信息。您可以将此包装在自定义的ILGenerator
类中,使用Emit
方法获取OpCode
参数,然后阅读OpCode.Size
和OpCode.Value
以获取相应的字节。
当然总是Mono.Cecil,但这可能需要对您已编写的代码进行更广泛的更改。
修改:you appear to have already figured this out yourself,但您将此问题保持打开状态。如果你自己想出来的话,你可以发布自己问题的答案并接受它们。这会让我知道我不应该浪费时间搜索,这会让其他人有同样的问题知道该怎么做。