前言:

经过前面几大部分的学习,已经掌握了 Emit 的前因后果,今天来详细讲解 Emit 中 IL 的部分内容。

如前文所讲,通过 DynamicMethod(或 MethodBuilder)可获得 ILGenerator 这个用于编写 IL 指令的类,并用它来编写 IL 指令。

本篇主要讲解 ILGenerator 的介绍,以及主要的辅助方法,详细的指令方法,则拆分到下一篇介绍。

下面就开邕它的介绍吧:

1、ILGenerator 介绍

ILGenerator 是.NET 中的一个重要组件,用于动态生成 Intermediate Language(IL)代码。

通过ILGenerator,开发人员可以在运行时创建和修改方法体内的IL指令,实现动态方法的生成和优化。

ILGenerator 提供了一组方法,允许程序员发出各种IL指令,包括加载、存储、运算、流程控制等操作,从而实现对方法体逻辑的灵活控制。

在 .NET 开发中,ILGenerator 通常与 DynamicMethod 类结合使用。

通过 DynamicMethod 创建动态方法对象,然后使用 ILGenerator 在其中生成IL代码。

这种结合使开发人员能够在运行时动态生成高效的代码,应用于一些需要动态生成代码的场景,如动态代理、AOP等。

ILGenerator 的灵活性和强大功能为.NET开发提供了更多可能性和自定义性。

2、ILGenerator 简单示例:

先看一下之前文章提到的代码:

 DynamicMethod dynamicMethod = new DynamicMethod("MyMethod", typeof(void), null);
ILGenerator il = dynamicMethod.GetILGenerator();
il.EmitWriteLine("hello world!");
il.Emit(OpCodes.Ret);

从示例代码使用了两类方法:

指令方法:il.Emit(OpCodes.Ret)
辅助方法:il.EmitWriteLine("hello world!")

所有的辅助方法,都是基于指令方法的封装,即用指令也可以实现该方法功能,

但用辅助方法,可以更简单的调用,下面开始介绍辅助方法。

3、ILGenerator 辅助方法:EmitWriteLine

该方法封装好的调用 WriteLine 输出控制台消息,使用它可以简单输出控制台方法,而不用编写 Emit 指令方法。

如果用 Emit 指令,编写是这样的:

            var il = methodBuilder.GetILGenerator();

            il.Emit(OpCodes.Ldstr, "这是一个示例消息");
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); il.Emit(OpCodes.Ret);

实现效果对应代码:

3、ILGenerator 辅助方法:异常处理 try catch finally

辅助方法中,提供了关于常用的 try catch finally 的封装方法,可以帮助我们更简单的编写IL方法:

看示例代码:

 var il = methodBuilder.GetILGenerator();

 il.BeginExceptionBlock();// 开始 try
il.EmitWriteLine("hello world!"); il.BeginCatchBlock(typeof(Exception));// 开始 catch
il.EmitWriteLine("hello world on error!"); il.BeginFinallyBlock();// 开始 finally
il.EmitWriteLine("hello world on finally!"); il.EndExceptionBlock();// 结束 il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);

参照对应生成的代码:

4、ILGenerator 辅助方法:异常抛出

辅助方法中,也提供了一个抛出异常的方法,示例代码:

var il = methodBuilder.GetILGenerator();

il.ThrowException(typeof(Exception));

il.Emit(OpCodes.Ret);

查看对应生成:

但是该辅助方法只能生成抛出异常,没有提供异常的参数。

如果需要更详细的异常抛出,则需要使用指令的方法:

指令方法如:

var il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldstr, "这是一个示例异常消息");

// 创建一个新的 Exception 实例
ConstructorInfo ctor = typeof(Exception).GetConstructor(new Type[] { typeof(string) });
il.Emit(OpCodes.Newobj, ctor); // 使用 ThrowException 方法引发异常
il.Emit(OpCodes.Throw); il.Emit(OpCodes.Ret);

生成的对应代码:

5、ILGenerator 辅助方法:定义变量

辅助方法中,有一个是用来辅助定义变量的。

但是它需要配置 Emit 指令使用,示例代码:

ILGenerator il = methodBuilder.GetILGenerator();

LocalBuilder local = il.DeclareLocal(typeof(string)); // 定义变量

il.Emit(OpCodes.Ldstr,"hello world"); // 加载字符串
il.Emit(OpCodes.Stloc, local); // 将字符串赋值给变量 il.Emit(OpCodes.Ldloc, local); //从变量中 加载值进栈
il.Emit(OpCodes.Ret);//返回(带值)

对应生成的代码:

在这个示例中(为了举例,多了中间的赋值取值的过程):

可以看出,定义的临时变量,都是没有名称的,只有类型。

它可以用来临时存值,需要用到的时候,再将值取出,对应两个 Emit 指令:

赋值:il.Emit(OpCodes.Stloc, local); // 将字符串赋值给变量
取值:il.Emit(OpCodes.Ldloc, local); //从变量中 加载值进栈

5、ILGenerator 辅助方法:定义标签

标签的定义,可以理解为跳转,即现在不常用的 goto 语句所需的的标签。

标签的定义,在 if else 中, switch 中,for 循环中,都会常常使用到标签。

标签的使用分为3步,定义标签、设定标签、跳转到标签。

标签定义:

Label label = il.DefineLabel();

设定标签:

il.MarkLabel(label);

跳转标签:

在 IL(Intermediate Language)中,可以使用以下指令来跳转到标签(Label):

条件跳转指令:

  1. beq:如果两个值相等,则跳转到指定的标签。
  2. bge:如果第一个值大于或等于第二个值,则跳转到指定的标签。
  3. bgt:如果第一个值大于第二个值,则跳转到指定的标签。
  4. ble:如果第一个值小于或等于第二个值,则跳转到指定的标签。
  5. blt:如果第一个值小于第二个值,则跳转到指定的标签.
  6. bne.un:如果两个无符号整数值不相等,则跳转到指定的标签。
  7. brtrue:如果值为 true,则跳转到指定的标签。
  8. brfalse:如果值为 false,则跳转到指定的标签。
  9. brtrue.s:如果值为 true,则跳转到指定的标签(短格式)。
  10. brfalse.s:如果值为 false,则跳转到指定的标签(短格式).

无条件跳转指令:

  1. br:无条件跳转到指定的标签。
  2. br.s:短格式的无条件跳转到指定的标签。
  3. leave:无条件跳转到 try、filter 或 finally 块的末尾。
  4. leave.s:短格式的无条件跳转到 try、filter 或 finally 块的末尾.

比较跳转指令:

  1. bgt.un:如果第一个无符号整数值大于第二个值,则跳转到指定的标签。
  2. bge.un:如果第一个无符号整数值大于或等于第二个值,则跳转到指定的标签。
  3. blt.un:如果第一个无符号整数值小于第二个值,则跳转到指定的标签。
  4. ble.un:如果第一个无符号整数值小于或等于第二个值,则跳转到指定的标签.

其他跳转指令:

  1. switch:根据给定的索引值跳转到不同的标签。
  2. brnull:如果值为 null,则跳转到指定的标签。
  3. brinst:如果对象是类的实例,则跳转到指定的标签。

这些指令可以帮助控制流程,在特定条件下跳转到指定的标签位置执行相应的代码。

通过合理使用这些跳转指令,可以实现复杂的逻辑控制和条件判断。

总结:

这篇教程总结了.NET Emit 中关于 IL 指令的第六部分,着重介绍了 ILGenerator 辅助方法的详细内容。

ILGenerator 是在动态生成程序集时用来生成 Intermediate Language(IL)指令的一个重要工具。

读者通过本篇文章,可以迅速了解到该教程的主要内容和重点,更好地掌握 ILGenerator 辅助方法的使用及 IL 指令的生成过程。

下一篇,我们将重点讲解 IL 的指令内容。

.NET Emit 入门教程:第六部分:IL 指令:2:详解 ILGenerator 辅助方法的更多相关文章

  1. Golang入门教程(十三)延迟函数defer详解

    前言 大家都知道go语言的defer功能很强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在 ...

  2. Docker入门教程(六)另外的15个Docker命令

    Docker入门教程(六)另外的15个Docker命令 [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第六篇,继续介绍Docker命令.之前的第二篇文章 ...

  3. 无废话ExtJs 入门教程十六[页面布局:Layout]

    无废话ExtJs 入门教程十六[页面布局:Layout] extjs技术交流,欢迎加群(201926085) 首先解释什么是布局: 来自百度词典的官方解释:◎ 布局 bùjú: [distributi ...

  4. Photoshop入门教程(六):通道

    学习心得:当大部分人听到通道.心里可能会有一种很害怕的感觉,因为“通道”并不像“图层”这样易于理解,望而生畏.”通道“的本质其实是存储图片的信息,把一张图片比作一个为网站,那么通道就是网站的后台,存储 ...

  5. RabbitMQ入门教程(十六):RabbitMQ与Spring集成

    原文:RabbitMQ入门教程(十六):RabbitMQ与Spring集成 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...

  6. ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解

    原文 ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 ...

  7. SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解

    SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解 博客分类: 跟开涛学SpringMVC   6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于 ...

  8. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  9. SaltStack 入门到精通第二篇:Salt-master配置文件详解

    SaltStack 入门到精通第二篇:Salt-master配置文件详解     转自(coocla):http://blog.coocla.org/301.html 原本想要重新翻译salt-mas ...

  10. 【入门】广电行业DNS、DHCP解决方案详解(三)——DNS部署架构及案

    [入门]广电行业DNS.DHCP解决方案详解(三)——DNS部署架构及案 DNS系统部署架构 宽带业务DNS架构 互动业务DNS架构 案例介绍 案例一 案例二 本篇我们将先介绍DNS系统部署架构体系, ...

随机推荐

  1. js根据输入字符长度自动调整textarea高度

    1.编写html <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  2. SpringCloud SpringBoot 组件使用:使用Nacos作为服务的注册中心和配置中心

    基础篇 一.什么是Nacos? 官方介绍是这样的: Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您实现动态服务发现.服务配置管理.服务及流量管理. Na ...

  3. 「源码分析」CopyOnWriteArrayList 中的隐藏的知识,你Get了吗?

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 前言 ...

  4. Hexo - 搭建个人博客的bug集合

    按照很多视频教程进行操作,发现到hexo d这一步后,无法部署到github远端. 目前的解决方法: npm un hexo-deployer-git npm i hexojs/hexo-deploy ...

  5. go经典知识及总结

    1.无论sync.Mutex还是其衍生品都会提示不能复制,但是能够编译运行 加锁后复制变量,会将锁的状态也复制,所以 mu1 其实是已经加锁状态,再加锁会死锁. 所以此题的答案是 fatal erro ...

  6. 使用3-hexo主题时无法正常渲染html代码

    问题描述 在hexo框架中使用3-hexo主题时,会遇到这样一个问题:在markdown中嵌入html代码,这些嵌入的html代码无法正常显示. 原因分析 在使用3-hexo主题时,默认使用主题自带的 ...

  7. 异常处理之raise A from B

    raise A from B 语句用于连锁chain异常 from 后面的B可以是: - 异常类 - 异常实例 - None 如果B是异常类或者异常实例,那么B会被设置为A的__cause__属性,表 ...

  8. 【LeetCode二叉树#09】路径总和I+II,以及求根节点到叶节点数字之和(回溯回溯,还是™的回溯)

    路径总和 力扣题目链接(opens new window) 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的 ...

  9. 08、Etcd 中MVCC原理

    本篇内容主要来源于自己学习的视频,如有侵权,请联系删除,谢谢. 1.什么是 MVCC MVCC(Multiversion concurrency control)是一个基于多版本技术实现的一种并发控制 ...

  10. 【Azure 应用服务】 部署到App Service for Linux 服务的Docker 镜像,如何配置监听端口呢?

    问题描述 根据以下DockerFile文件,创建了一个ASP.NET Core的 helloworld 镜像,通过监听3721端口来接受请求. # 1. 指定编译和发布应用的镜像 FROM mcr.m ...