.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 代码的更多相关文章

  1. 如何快速编写和调试 Emit 生成 IL 的代码

    .NET Core/.NET Framework 的 System.Reflection.Emit 命名空间为我们提供了动态生成 IL 代码的能力.利用这项能力,我们能够在运行时生成一段代码/一个方法 ...

  2. Emit 自动生成IL代码,注入代码

    Spring 框架中的注入代码,以及自动生成对接口的实现,则根据il代码注入 Emit学习(1)-Emit概览 一.Emit概述 Emit,可以称为发出或者产生.在Framework中,与Emit相关 ...

  3. 【抬杠.NET】如何进行IL代码的开发

    背景 在有些时候,由于C#的限制,或是追求更高的性能,我们可以编写IL代码来达到我们的目的.本文将介绍几种IL代码开发的几种方式,环境为visual studio 2019 + net5.0 sdk. ...

  4. 【抬杠.NET】如何进行IL代码的开发(续)

    背景 之前写了一篇文 [抬杠.NET]如何进行IL代码的开发 介绍了几种IL代码的开发方式. 创建IL项目 C#项目混合编译IL 使用InlineIL.Fody 使用DynamicMethod(ILG ...

  5. 如何通过ildasm/ilasm修改assembly的IL代码

    原文地址:http://kb.cnblogs.com/page/101162/ 这段时间为跟踪一个Bug而焦头烂额,最后发现是Framework的问题,这让人多少有些绝望.所以到微软论坛提了个帖子,希 ...

  6. Ngen生成Native代码实战及优缺点分析

    先科普一下,.Net是一个用于Windows的托管代码模型,用于高效构建具有视觉上引人注目的用户体验的应用程序.但这个模型生成的代码并非可执行代码,而是由.Net公共语言运行库环境执行的IL代码.所以 ...

  7. .net postsharp编译时生成的代码?

    使用PostSharp进行AOP框架设计:一个简单的原型   AOP已经不是一个什么新名词了,在博客园使用关键字搜索可以查出n多条关于AOP的介绍,这里就不再赘述了. 在Bruce Zhang's B ...

  8. 一、源代码-面向CLR的编译器-托管模块-(元数据&IL代码)

    本文脉络图如下: 1.CLR(Common Language Runtime)公共语言运行时简介 (1).公共语言运行时是一种可由多种编程语言一起使用的"运行时". (2).CLR ...

  9. 【小白学C#】浅谈.NET中的IL代码

    一.前言 前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的 ...

随机推荐

  1. Git 设置 SOCKS 代理

    $ export all_proxy=socks5://127.0.0.1:1080

  2. gcc,gdb,make学习

    实例学习gcc+gdb+make程序编译.链接.运行时头文件或动态链接库的查找 分四步: 预处理.编译.汇编.链接4steps:preprocess,compile,assemble,link ​

  3. 奇怪的表达式求值 (java实现)

    题目参考:http://blog.csdn.net/fuxuemingzhu/article/details/68484749 问题描述; 题目描述: 常规的表达式求值,我们都会根据计算的优先级来计算 ...

  4. 个人知识管理系统Version1.0开发记录(03)

    demo  设 计 一个知识点demo,在数据库和用户界面的互动事件.分三个层次,数据存储,数据方法工具,数据呈现界面.这一次先完成数据存储,按以下逻辑实现.工具:eclipse,oracle数据库, ...

  5. neutron源码分析(一)OpenStack环境搭建

    一.OpenStack安装 安装一个初始化的Mitaka版本的OpenStack环境用于分析,neutron源码 序号 角色 IP地址 版本 1 controller 172.16.15.161 mi ...

  6. 伪基站,卒于5G——本质上是基于网络和UE辅助的伪基站检测,就是将相邻基站的CI、信号强度等信息通过测量报告上报给网络,网络结合网络拓扑、配置信息等相关数据,对所有数据进行综合分析,确认在某个区域中是否存在伪基站

    伪基站,卒于5G from:https://www.huxiu.com/article/251252.html?h_s=h8 2018-07-05 21:58收藏27评论6社交通讯     本文来自微 ...

  7. ts结合vue使用的感悟

    TypeScript 前端现在越来越强大,多人开发更是常见,加上各大框架都开始支持TypeScript,而谷歌和微软又更加积极,导致不得不去学习,顺道通过js来了解ts,再通过ts来了解强类型语言.一 ...

  8. ssh自动分发密匙脚本样板

    #!/bin/bash rom=/media/cdrom dir=/etc/yum.repos.d Use=$ mima=$ function yumj(){ [ -d "$rom" ...

  9. hdu2853

    题解: KM算法模板 然后我把另一边加了点 然后写了#define int long long 然后莫名挂... 然后去掉就过了 代码: #include<cstdio> #include ...

  10. Ubuntu 中sendmail 的安装、配置与发送邮件的具体实现

    一.安装 ubuntu中sendmail函数可以很方便的发送邮件,ubuntu sendmail先要安装两个包. 必需安装的两个包: 代码  sudo apt-get install sendmail ...