使用 Emit 生成 IL 代码
.NET Core/.NET Framework 的 System.Reflection.Emit 命名空间为我们提供了动态生成 IL 代码的能力。利用这项能力,我们能够在运行时生成一段代码/一个方法/一个类/一个程序集。
本文将介绍使用 Emit 生成 IL 代码的方法,以及在此过程中可能遇到的各种问题。
在编写以下代码时如果遇到一些意料之外的错误,希望调试生成的 IL 代码,可以尝试阅读 如何快速编写和调试 Emit 生成 IL 的代码 - walterlv 了解如何调试和解决。
用 Emit 生成 IL 代码时,很多我们写 C# 时不会注意到的问题现在都需要开始留意。
在阅读本文之前,希望统一一个平时可能不太留意的英文:
- 形参:parameter
- 实参:argument
如果不了解它们之间的区别,请自行搜索。
定义方法签名
在 IL 中,方法名称可以使用比 C# 更多的字符,例如“<”和“>”,这也是 C# 编译闭包时喜欢使用的字符。目前我还没有找到 IL 中哪些字符可以作为标识符名称,但从混淆工具来看,是比 C# 多得多的。
如果你试图生成实例方法,那么实例本身 this 将成为第一个参数,不过并不需要额外将它定义到参数列表中。
当然,如果是静态方法,我们能够自己指定一个 this 参数,不过没有实际的意义。
var method = new DynamicMethod("<MethodName>",
typeof(void), new[] { typeof(object), typeof(object) });
method.DefineParameter(1, ParameterAttributes.None, "this");
method.DefineParameter(2, ParameterAttributes.None, "value");
如果不声明形参,那么生成的 IL 代码的函数将无法被正常调用(提示可能造成运行时的不稳定)。
声明和初始化局部变量
平时写 C# 的时候,可能一个方法里面没有定义任何一个局部变量,但 IL 可不一定这么认为。
例如:
int a = 0;
if (value.GetType() == typeof(string))
{
}
else
{
}
实际上,在 IL 中,除了 Int32 类型的 a 之外,还会额外定义一个 bool 类型的局部变量 V_1。在 value.GetType() == typeof(string) 执行完后,其值将存入 V_1。
所以,如果需要编写 Emit 生成代码的代码,需要注意这些隐式产生的局部变量,它们需要和普通变量一样被初始化。
Emit 代码为:
// 这就声明了两个局部变量。
il.DeclareLocal(typeof(int));
il.DeclareLocal(typeof(bool));
定义标签
如果代码中存在非线性结构,例如 if-else,那么 IL 就需要知道跳转的地址。那么如何能够知道跳转到哪个地址呢?
——使用标签。
对 if-else 来说,if 操作需要知道 else 的起始地址;对于 if 内部的结尾来说,需要知道整个 if-else 结束之后的第一个操作的地址。
var startOfElse = il.DefineLabel();
var endOfWholeIfElse = il.DefineLabel();
il.Emit(OpCodes.Nop);
// 其他生成代码。
// 如果 if 条件不满足,跳转到 startOfElse。
il.Emit(OpCodes.Brfalse_S, startOfElse);
// 其他生成代码。
// 在 if 结束之后,跳转到 endOfWholeIfElse 地址。
il.Emit(OpCodes.Br_S, endOfWholeIfElse);
// 其他生成代码。
// 假设这里到了 else 的开头了,于是将 startOfElse 进行标记。标记完紧跟着写 else 部分的代码。
il.MarkLabel(startOfElse);
il.Emit(OpCodes.Nop);
// 其他生成代码。
// 假设这里整个 if-else 结束了,于是将 endOfWholeIfElse 进行标记。
il.MarkLabel(endOfWholeIfElse);
参考资料
- 生成方法签名与元数据
- 生成方法体
使用 Emit 生成 IL 代码的更多相关文章
- 如何快速编写和调试 Emit 生成 IL 的代码
.NET Core/.NET Framework 的 System.Reflection.Emit 命名空间为我们提供了动态生成 IL 代码的能力.利用这项能力,我们能够在运行时生成一段代码/一个方法 ...
- Emit 自动生成IL代码,注入代码
Spring 框架中的注入代码,以及自动生成对接口的实现,则根据il代码注入 Emit学习(1)-Emit概览 一.Emit概述 Emit,可以称为发出或者产生.在Framework中,与Emit相关 ...
- 【抬杠.NET】如何进行IL代码的开发
背景 在有些时候,由于C#的限制,或是追求更高的性能,我们可以编写IL代码来达到我们的目的.本文将介绍几种IL代码开发的几种方式,环境为visual studio 2019 + net5.0 sdk. ...
- 【抬杠.NET】如何进行IL代码的开发(续)
背景 之前写了一篇文 [抬杠.NET]如何进行IL代码的开发 介绍了几种IL代码的开发方式. 创建IL项目 C#项目混合编译IL 使用InlineIL.Fody 使用DynamicMethod(ILG ...
- 如何通过ildasm/ilasm修改assembly的IL代码
原文地址:http://kb.cnblogs.com/page/101162/ 这段时间为跟踪一个Bug而焦头烂额,最后发现是Framework的问题,这让人多少有些绝望.所以到微软论坛提了个帖子,希 ...
- Ngen生成Native代码实战及优缺点分析
先科普一下,.Net是一个用于Windows的托管代码模型,用于高效构建具有视觉上引人注目的用户体验的应用程序.但这个模型生成的代码并非可执行代码,而是由.Net公共语言运行库环境执行的IL代码.所以 ...
- .net postsharp编译时生成的代码?
使用PostSharp进行AOP框架设计:一个简单的原型 AOP已经不是一个什么新名词了,在博客园使用关键字搜索可以查出n多条关于AOP的介绍,这里就不再赘述了. 在Bruce Zhang's B ...
- 一、源代码-面向CLR的编译器-托管模块-(元数据&IL代码)
本文脉络图如下: 1.CLR(Common Language Runtime)公共语言运行时简介 (1).公共语言运行时是一种可由多种编程语言一起使用的"运行时". (2).CLR ...
- 【小白学C#】浅谈.NET中的IL代码
一.前言 前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的 ...
随机推荐
- 【Python】“UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9”根因及解决方法
背景 自动化测试调用HTMLTestRunner生成测试报告的时候,出现了编码错误,错误如题 原因 搜索了很多资料,得出的结论是python的str默认是ascii编码,和unicode编码冲突,就会 ...
- 【转】python操作mysql数据库
python操作mysql数据库 Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口. Python 数据库接口支持非常多的数据库 ...
- 热备模式相关问题2.txt
--//上午测试热备模式相关问题,就是如果打开热备模式,如果中间的归档丢失,oracle在alter database end backup ;时并没有应用日志. --//虽然热备份模式文件头scn被 ...
- Docker 生态
Docker 和容器技术的发展可谓是日新月异,本文试图以全局的视角来梳理一下 docker 目前的生态圈.既然是概览,所以不会涉及具体的技术细节. Docker 自从发布以来发生了很多的变化,并且有些 ...
- write 系统调用耗时长的原因
前一阵子公司一部门有人叫帮忙调查,说他们write系统调用基本上是个位数微秒就返回,或者说几十us,但偶尔出现几次write系统调用达到几百毫秒和情况.大家都知道,通过vfs进行write,都是写写到 ...
- spring配置bean的生命周期
配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http:// ...
- IOS-小技巧总结,绝对有你想要的
1.App名称的修改 许多个人开发者或许会有和我一样的经历,开发一个App途中会想到更合适的名字,这时候变会修改工程名以达到App名称改变的目的,其实你可以一步到位—— 在info.plist中添加一 ...
- Highcharts 散点图
Highcharts 散点图 配置 chart 配置 配置 chart 的 type 为 'scatter' .chart.type 描述了图表类型.默认值为 "line". ch ...
- Post with HttpClient4
转载:http://www.cnblogs.com/luxiaoxun/p/6165237.html 作者:阿凡卢 出处:http://www.cnblogs.com/luxiaoxun/ HttpC ...
- laravel中的validate验证的使用案例:
第一个是设置,第二个是直接调用.