使用 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#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的 ...
随机推荐
- 关于javascript以及jquery如何打开文件
其实很简单, <input type="file" id="file" mce_style="display:none"> 这个 ...
- Outlook 配置qq邮箱账号
最近想用Outlook 2013管理QQ邮件,配置好久都没有成功,结果最后发现第三方登陆QQ邮箱不使用QQ密码,而是使用一个叫”授权码”的东西.(用户名自动生成的,授权码就填这,报错后填会测试不通过) ...
- 超详细!mac flutter 创建过程及遇到的问题
虽然网上有教程,但是过程中遇到些问题,这些问题教程里并没有,所以写这个文章记录一下. 1.打开终端 2.clone flutter 命令: git clone -b beta https://gith ...
- 【Python】模块学习之Timer定时任务,递归定时自调获取博客浏览量
Timer定时任务 下面是Timer函数的官方doc介绍信息 """ Call a function after a specified number of second ...
- python学习大纲目录(转自alex博客https://www.cnblogs.com/alex3714/)
day01: 介绍.基本语法.流程控制 Python介绍 发展史 Python 2 or 3? 安装 Hello World程序 变量 用户输入 模块初识 .pyc是个什么鬼? 数据类型初识 数据运算 ...
- 2-1 RHEL6.5 环境搭建与部署
第二部分:Linux常见服务管理 2-1 RHEL6.5 环境搭建与部署 第二部分主要讲解的是开源服务搭建 学习方法与注意事项: 1. 端正态度,开始学习 2. 认真完成作业和实验(并详细记录) 3. ...
- POJ-3659-最小支配集裸题/树形dp
Cell Phone Network Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7127 Accepted: 254 ...
- opencv颜色提取color filting
# -*- coding: utf-8 -* import numpy as np import cv2 cap = cv2.VideoCapture(0) while True: _ , frame ...
- 【hive】解析json格式字符串
(1)解析json中的单个属性 get_json_object(json_str,’$.xxx’/‘$[xxx]’) get_json_object函数第一个参数填写json对象变量(string) ...
- laravel中的validator()类验证的使用