前言:

经过前面几篇的学习,我们了解到指令的大概分类,如:

参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令。

参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变量中,以方便后续使用。

创建实例指令,其指令以 New 开头,用于在运行时动态生成并初始化对象。

方法调用指令,该指令以 Call 开头,用于在运行时调用其它方法。

支条件指令,该指令通常以 Br、或 B、C 开头,用于在运行分支条件时跳转指令。

本篇介绍类型转换指令,该指令通常以 Cast、Conv 开头或box结尾,用于在运行时对类型进行转换。

类型转换指令介绍:

在.NET中,类型转换是一个常见的操作,它允许我们在不同的数据类型之间进行转换。ILGenerator 提供了一系列的指令来执行各种类型转换操作。这些指令可以分为三类:强制类型转换指令、隐式类型转换指令和数值类型转换指令。

  1. 强制类型转换指令:这些指令用于执行显式的类型转换操作,如果转换失败则会抛出异常。常见的强制类型转换指令包括 castclass 和 isinst 指令。

  2. 隐式类型转换指令:这些指令用于执行从引用类型到值类型或者从值类型到引用类型的转换,或者在值类型之间执行转换。unboxbox 指令是常见的隐式类型转换指令。

  3. 数值类型转换指令:这些指令用于执行不同数值类型之间的转换,比如将整数转换为浮点数,或者将浮点数转换为整数。conv 指令系列提供了这些功能。

通过这些类型转换指令,我们可以在 IL 级别执行各种类型转换操作,为动态生成的代码增加了灵活性和功能性。

接下来我们将详细介绍这些指令的用法和示例。

1、强制类型转换指令:

Castclass 指令:强制类型转换

示例代码:

MethodBuilder methodBuilder = tb.DefineMethod("ConvertTo", MethodAttributes.Public | MethodAttributes.Static, typeof(XmlEntity), new Type[] { typeof(object) });

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0 );
il.Emit(OpCodes.Castclass, typeof(XmlEntity)); il.Emit(OpCodes.Ret); // 返回该值

对应代码:

Isinst 指令: as 类型转换

示例代码:

MethodBuilder methodBuilder = tb.DefineMethod("ConvertTo", MethodAttributes.Public | MethodAttributes.Static, typeof(XmlEntity), new Type[] { typeof(object) });
ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0 );
il.Emit(OpCodes.Isinst, typeof(XmlEntity)); il.Emit(OpCodes.Ret); // 返回该值

对应代码:

2、隐式类型转换指令:

1、Box 指令:装箱

示例代码:

MethodBuilder methodBuilder = tb.DefineMethod("ConvertTo", MethodAttributes.Public | MethodAttributes.Static, typeof(object), new Type[] { typeof(int) });
ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Box, typeof(int)); il.Emit(OpCodes.Ret); // 返回该值

对应代码:

2、Unbox_Any 指令:拆箱

示例代码:

MethodBuilder methodBuilder = tb.DefineMethod("ConvertTo", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(object) });

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Unbox_Any, typeof(int)); il.Emit(OpCodes.Ret); // 返回该值

对应代码:

3、Unbox 指令:拆箱并返回指向值的引用地址

对应代码:

可以看出,返回的是引用,如果需要获取值,需要配置 Ldobj 指令:

来一个示例代码:

var dynamicMethod = new DynamicMethod("ConvertTo", typeof(int), new Type[] { typeof(object) }, typeof(AssMethodIL_Condition));

ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Unbox, typeof(int));
il.Emit(OpCodes.Ldobj, typeof(int)); il.Emit(OpCodes.Ret); // 返回该值 var result = dynamicMethod.Invoke(null, new object[] { 11 });
Console.WriteLine(result);
Console.Read();

运行效果:

可以理解为:

Unbox_Any 指令 = Unbox 指令 + Ldobj 指令。 

3、数值类型转换指令:

在CIL(Common Intermediate Language)中,"conv"(convert)相关指令用于进行类型转换,将一个数据类型转换为另一个数据类型。这些指令通常用于在不同的数据类型之间进行显式转换。

以下是一些常用的"conv"相关指令及其功能:

  1. conv.i1: 将值转换为有符号 8 位整数类型(sbyte)。
  2. conv.i2: 将值转换为有符号 16 位整数类型(short)。
  3. conv.i4: 将值转换为有符号 32 位整数类型(int)。
  4. conv.i8: 将值转换为有符号 64 位整数类型(long)。
  5. conv.u1: 将值转换为无符号 8 位整数类型(byte)。
  6. conv.u2: 将值转换为无符号 16 位整数类型(ushort)。
  7. conv.u4: 将值转换为无符号 32 位整数类型(uint)。
  8. conv.u8: 将值转换为无符号 64 位整数类型(ulong)。
  9. conv.r4: 将值转换为单精度浮点数类型(float)。
  10. conv.r8: 将值转换为双精度浮点数类型(double)。

这些指令在IL代码中用于执行类型转换操作。下面是一个简单的示例,演示如何使用这些指令:

var dynamicMethod = new DynamicMethod("ConvertTo", typeof(float), new Type[] { typeof(int) }, typeof(AssMethodIL_Condition));
ILGenerator il = dynamicMethod.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Conv_R4); il.Emit(OpCodes.Ret); // 返回该值 var result = dynamicMethod.Invoke(null, new object[] { 11 });
Console.WriteLine(result);
Console.Read();

运行结果:

在 Emit 中,类型是需要精确转换的,如果不进行转换,你可能得到类似这样的结果:

总结:

在本教程的第六部分中,我们深入探讨了 ILGenerator 中的类型转换指令。

通过了解这些指令,你可以在动态生成的代码中执行各种类型转换操作,从而更好地控制程序的行为和数据流。

类型转换指令在 .NET 开发中非常有用,特别是在需要进行数据类型转换或操作时。

通过本教程,你应该已经了解了如何使用 ILGenerator 来生成这些转换指令,并且知道它们在 IL 代码中的具体用法和语法。

掌握 ILGenerator 中的类型转换指令将为你的动态代码生成带来更大的灵活性和效率。

继续学习并探索 ILGenerator 中其他功能和指令,以加深对 .NET 平台底层运行机制的理解,并提升自己在 .NET 开发领域的技能水平。

.NET Emit 入门教程:第六部分:IL 指令:8:详解 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. 从0开始入门智能知识库和星火大模型,打造AI客服。

    介绍FastWiki FastWiki是一个高性能.基于最新技术栈的知识库系统,旨在为大规模信息检索和智能搜索提供解决方案.它采用微软Semantic Kernel进行深度学习和自然语言处理,在后端使 ...

  2. Educational Codeforces Round 143 (Rated for Div. 2)C. Tea Tasting(前缀和+二分、贡献枚举)

    C. Tea Tasting 思路 这里枚举有三种思路 然后经过考虑3是最可行的,然后接着考虑如何计算贡献 这里在实现的时候用了一个差分数组,因为我们需要记录第i个茶师它喝了多少个\(b_i\)以及不 ...

  3. Codeforces Round 345 (Div. 1)A. Watchmen(容斥原理)

    A. Watchmen 当欧几里得距离和曼哈顿距离相等时,\(x1==x2||y1==y2\) 这两个条件满足其一.这和容斥原理一样,至少选择一个的条件. 我们可以计算xi,以及小于i之前的这些,这样 ...

  4. 《Relation of the Relations A New Paradigm of the Relation Extraction Problem》论文阅读笔记

    原文 代码 摘要 为了解决传统的关系抽取(RE)方法只能识别两个实体之间的关系,而忽略了同一上下文中多个关系之间的相互依赖性,即关系的关系(relation of relations,RoR)的问题, ...

  5. foundation部分学习记录(更正更新中……)

    foundation部分学习记录(更新中--) 从FDB的角度看,它对上层只提供有序+事务+KV存储的抽象. 设计原则 模块化分割,尽量细分且模块之间相互解耦 例如事务系统内,其提交(write pa ...

  6. python 生成虚拟环境requirement.txt

    # 在项目根目录中,打开终端执行以下命令 # 生成 requirements.txt 文件 pip3 freeze > requirements.txt # 安装依赖文件 pip3 instal ...

  7. vscode 注释后 光标下移(注释后光标快速定位下一行) 仿照idea

    VSCode 注释后光标快速定位下一行 1. 安装插件 Multi-command Multi-command 2.打开 Keybinding.json Keybinding.json 左下角齿轮图标 ...

  8. Linux系统设置shell开机自启

        自己写一个shell脚本 chmod -x file.sh sudo cp file.sh /etc/profile.d/ 将写好的脚本(.sh文件)放到目录 /etc/profile.d/ ...

  9. SqlServer复制和订阅(实现主从同步)

    SqlServer复制和订阅 注意: 1.登录必须是服务器名称不能是ip 2.订阅服务器不需要提前创建数据库 复制 1.展开要发布的数据库节点,找到复制下的本地发布 2.右击本地发布,选择本地发布 3 ...

  10. STM32 SPI DMA 源码解析及总结

    一 前言 最近在调试stm32的SPI时候i,遇到了一个非常诡异的问题.中间花费了不少时间才把问题搞定.这中间暴露的问题值得反思.借此机会,还是梳理一下stm32的SPI的代码做一个总结吧. 二 初始 ...