前言:

上一篇介绍了 IL 指令的分类以及参数加载指令,该加载指令以ld开头,将参数加载到栈中,以便于后续执行操作命令。

本篇开始介绍参数存储指令,其指令以st开头,将栈中的数据,存储到指定的变量中,以方便后续使用。

参数存储指令介绍:

在 IL 中,除了参数存储指令 stargstloc 之外,还有其他一些以 "st" 开头的指令,如 stfldstsfld,它们也用于存储值到特定位置。以下是所有的参数存储指令以及它们的用途:

  1. starg index:将计算堆栈顶部的值存储到方法的参数中,参数索引由后续字节指定。

  2. stloc index:将计算堆栈顶部的值存储到方法的局部变量中,局部变量索引由后续字节指定。

  3. stfld field:将计算堆栈顶部的值存储到对象的字段中,字段由元数据标识指定。

  4. stsfld field:用于将值存储到静态字段(static field)中。静态字段是属于类本身而不是类的实例的字段,它们在整个应用程序生命周期内只有一份拷贝,被所有实例共享。

这些指令都是用于在 IL 中进行值的存储操作,用途包括更新方法参数、修改局部变量值、设置对象字段值以及修改数组元素。它们在方法体中起到了关键的作用,用于实现各种数据操作和赋值操作。

1、存储指令:starg

  1. starg.s index:将计算堆栈顶部的值存储到方法的参数中,参数索引由单字节指定(适用于参数索引小于 256 的情况)。

  2. starg index:将计算堆栈顶部的值存储到方法的参数中,参数索引由后续字节指定(适用于参数索引大于等于 256 的情况)。

该指令为:store argument 存储参数的简写。

示例代码:

var dynamicMethod = new DynamicMethod("GetValue", typeof(object), new[] { typeof(string) }, typeof(AssMethodIL_ST));

var ilGen = dynamicMethod.GetILGenerator();

// 使用 starg 指令将方法参数值传递给局部变量
ilGen.Emit(OpCodes.Ldstr,"abc");
ilGen.Emit(OpCodes.Starg, 0); // 将方法的第一个参数值传递给局部变量 // 返回局部变量的值
ilGen.Emit(OpCodes.Ldarg_0); // 加载第一个参数(message)
ilGen.Emit(OpCodes.Ret); // 返回该值

该示例的代码,主要体现在对参数重新赋值,对应的方法原型:

public static object GetValue(string arg)
{
arg = "abc";
return arg;
}

2、存储指令:stloc

stloc index:将计算堆栈顶部的值存储到方法的局部变量中,局部变量索引由后续字节指定。

该指令为:store local 存储本地(变量)的简写。

该方法需要配合辅助变量使用,这个在上一篇辅助方法中有介绍到,这里重温一下上上篇的辅助方法,定义变量的内容:

该系列指令中,还有stloc_0、stloc_1、stloc_2、stloc_3,代表定义的第N个临时变量。

该变量的定义在反编绎 IL 中可以对照 .locals init 内容:

3、存储指令:stfld

stfld field:将计算堆栈顶部的值存储到对象的字段中,字段由元数据标识指定。

该参数为:store filed 存储字段的简写,其常用于给字段变量赋值。

下面举一个给成员变量赋值的示例:

Entity entity = new Entity();

FieldInfo idInfo = typeof(Entity).GetField("ID");

var dynamicMethod = new DynamicMethod("SetValue", typeof(void), new[] { typeof(Entity), typeof(int) }, typeof(AssMethodIL_ST));

var ilGen = dynamicMethod.GetILGenerator();

ilGen.DeclareLocal(typeof(Entity));

ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Stfld, idInfo);
ilGen.Emit(OpCodes.Ret); dynamicMethod.Invoke(null, new object[] { entity, 111 }); Console.WriteLine(entity.ID);
Console.Read();

运行结果:

方法原型对照:

小说明:

stfld 指令是成员变量的赋值,而我们常用的属性赋值【get】或取值【set】,对应的是方法调用,因此属性的相关操作会在方法调用指令一文中再述。

4、存储指令:stsfld

该参数为:store static filed 存储静态字段的简写,其常用于给静态字段变量赋值。

示例代码:

public class Entity { public int ID; public static int ID2; }
public static void D3()
{
Entity entity = new Entity(); FieldInfo idInfo = typeof(Entity).GetField("ID2", BindingFlags.Static| BindingFlags.Public); var dynamicMethod = new DynamicMethod("SetValue", typeof(void), new[] { typeof(int) }, typeof(AssMethodIL_ST)); var ilGen = dynamicMethod.GetILGenerator(); ilGen.DeclareLocal(typeof(Entity)); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Stsfld, idInfo); // 加载第一个参数(message)
ilGen.Emit(OpCodes.Ret); // 返回该值 dynamicMethod.Invoke(null, new object[] { 222 }); Console.WriteLine(Entity.ID2);
Console.Read();
}

运行结果:

总结:

相比于参数加载指令的类型复杂度,参数存储指令则相对简约许多。

总的来说,参数存储指令的重要性在于它为动态生成代码提供了强大的参数处理能力,让开发者可以更加灵活地操作方法的参数,实现更加复杂和多样化的编程逻辑。

通过合理运用参数存储指令,我们可以实现更加高效、智能和灵活的动态代码生成过程。

.NET Emit 入门教程:第六部分:IL 指令:4:详解 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. 2021-07-01 原生js获取文件数据

    原理 手动用js创建一个type为file的DOM元素. 在读取到数据后,清空手动创建的DOM元素.返回得到的Promise类型的文件数据files. const getFilesPromise = ...

  2. Jenkins流水线使用@Grab 导入Maven库

    有个需求需要在pipeline中调用Java的SDK去执行业务 使用 @Grab 注解可以在Maven中导入Java 库, @Grab('org.apache.commons:commons-math ...

  3. 【算法day6】哈希表、有序表、链表(反转单链表)

    哈希表的简单介绍 1)哈希表在使用层面上可以理解为一种集合结构 2)如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet) 3)如果既有key,又有伴 ...

  4. 第129篇:JS模块化开发

    好家伙,本篇为<JS高级程序设计>第二十六章"模块"学习笔记 JS开发会遇到代码量大和广泛使用第三方库的问题. 解决这个问题的方案通 常需要把代码拆分成很多部分,然后再 ...

  5. 【Azure 存储服务】使用POST方式向Azure Storage Queue中插入Message的办法

    问题描述 使用POST HTTP Request, 如何向Azure Storage Queue中写入Message呢?例如使用CURL发送POST指令是否可以呢? CURL -H "Con ...

  6. 【夏令时】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天

    前言 缘由 用@JsonFormat(pattern = "yyyy-MM-dd")注解,出生日期竟然年轻了一天 艺术源于生活,bug源于开发. 起因是艰苦奋战一个月,测试及验收都 ...

  7. C++//vector存放自定义数据类型

    1 //vector存放自定义数据类型 2 3 #include <iostream> 4 #include <string> 5 #include<fstream> ...

  8. Codeforces Round 170 (Div. 1)A. Learning Languages并查集

    如果两个人会的语言中有共同语言那么他们之间就可以交流,并且如果a和b可以交流,b和c可以交流,那么a和c也可以交流,具有传递性,就容易联想到并查集,我们将人和语言看成元素,一个人会几种语言的话,就将这 ...

  9. .net中最简单的http请求调用(比如调用chatgpt的openAI接口)

    支持.Net Core(2.0及以上)/.Net Framework(4.5及以上),可以部署在Docker, Windows, Linux, Mac. http请求调用是开发中经常会用到的功能,因为 ...

  10. Android学习之文件存储

    •前言 任何一个应用程序,其实说白了就是在不停地和数据打交道,我们聊QQ.看新闻.刷微博,所关心的都是里面的数据, 没有数据的应用程序就变成了一个空壳子,对用户来说没有任何实际用途. 那么这些数据都是 ...