当前位置: 首页 > news >正文

使用 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);

参考资料

  • 生成方法签名与元数据
    • ParameterBuilder Class (System.Reflection.Emit)
    • MethodBuilder.DefineParameter Method (Int32, ParameterAttributes, String) (System.Reflection.Emit)
    • Defining a Parameter with Reflection Emit
    • c# - How to set “.maxstack” with ILGenerator - Stack Overflow
  • 生成方法体
    • ILGenerator.DefineLabel Method (System.Reflection.Emit)
    • ILGenerator.MarkLabel Method (Label) (System.Reflection.Emit)
    • c# - Emit local variable and assign a value to it - Stack Overflow
    • C# reflection: If … else? - Stack Overflow

相关文章:

  • 如何快速编写和调试 Emit 生成 IL 的代码
  • 自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
  • 冷算法:自动生成代码标识符(类名、方法名、变量名)
  • WPF/UWP 的 Grid 布局竟然有 Bug,还不止一个!了解 Grid 中那些未定义的布局规则
  • Git 更安全的强制推送,--force-with-lease
  • 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)
  • 理解 C# 项目 csproj 文件格式的本质和编译流程
  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
  • 如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
  • 每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译
  • C# 中那些可以被重载的操作符,以及使用它们的那些丧心病狂的语法糖
  • 神器如 dnSpy,无需源码也能修改 .NET 程序
  • Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • ES6指北【2】—— 箭头函数
  • 【node学习】协程
  • Angular 4.x 动态创建组件
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • HTTP那些事
  • Javascript 原型链
  • JSONP原理
  • Python3爬取英雄联盟英雄皮肤大图
  • Vue 动态创建 component
  • 构造函数(constructor)与原型链(prototype)关系
  • 批量截取pdf文件
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​configparser --- 配置文件解析器​
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #宝哥教你#查看jquery绑定的事件函数
  • (4)STL算法之比较
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (搬运以学习)flask 上下文的实现
  • (六)Hibernate的二级缓存
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)插入排序
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • ./和../以及/和~之间的区别
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • @ResponseBody
  • @Transactional类内部访问失效原因详解
  • [20161101]rman备份与数据文件变化7.txt
  • [ABC294Ex] K-Coloring
  • [ai笔记3] ai春晚观后感-谈谈ai与艺术
  • [C puzzle book] types
  • [daily][archlinux][game] 几个linux下还不错的游戏
  • [EMWIN]FRAMEWIN 与 WINDOW 的使用注意
  • [HTML]Web前端开发技术12(HTML5、CSS3、JavaScript )——喵喵画网页
  • [JS] 常用正则表达式集(一)
  • [Linux]进程间通信(system V共享内存 | system V信号量)
  • [Linux_IMX6ULL应用开发]-Makefile