前言:

上一篇介绍了 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. win32 - 匿名管道的使用

    目标: 创建一个父进程和子进程,在子进程的控制台窗口输入数据,数据通过管道发送给父进程,父进程的控制台窗口读取数据,最后将数据打印出来. Parent.cpp //CMD.exe #include & ...

  2. Kubernetes leader election 源码分析

    0. 前言 Kubernetes:kube-scheduler 源码分析 介绍了 kube-scheduler 调度 Pod 的逻辑.文中有一点未提的是,在 Kubernetes 集群中,kube-s ...

  3. 常见的问题系列--- Swagger @ApiOperationSupport忽略失效的问题

    https://www.cnblogs.com/hujunwei/p/15853307.html

  4. Celery异步处理任务时遇到的错误ValueError: not enough values to unpack (expected 3, got 0)

    开启celery异步,终端命令: celery -A celery_tasks.main worker -l info 如果上面运行后,发送短信码的时候没有报如下错误: ValueError: not ...

  5. JS内存爆破问题

    原理 检测到调试,格式化等,疯狂的在js文件,或者html中进行读写,cookie重写追加,字节追加,导致内存不足够,卡死 内存爆破,指js通过死循环/频繁操作数据库(包括cookie)/频繁调取hi ...

  6. 【Azure Redis 缓存 Azure Cache For Redis】Azure Redis删除 TLS 1.0 和 1.1的计划及问题

    问题描述 Azure Redis 正式关闭TLS1.0和1.1关闭的时间 根据文档描述Azure Redis阶段2我们将停止支持 TLS 1.1 和 TLS 1.0,暂时计划 2020 年 12 月 ...

  7. 【Azure Developer】使用 Powershell az account get-access-token 命令获取Access Token (使用用户名+密码)

    问题描述 在上篇的文章中,我们使用了JAVA SDK,根据用户名和密码来获取Azure AD的Access Token,这节,我们将使用Powershell az 命令来获取Access Token. ...

  8. 1、eureka的注册流程

    客户端注册到服务端是通过http请求的 涉及到多级缓存 register注册表 源码精髓:多级缓存设计思想 在拉取注册表的时候: 首先从ReadOnlyCacheMap里查缓存的注册表. 若没有,就找 ...

  9. 设计原则 --- SOLID

    引言 SOLID是五个常见的面向对象设计原则的缩写,其目的是帮助开发者设计易于维护和扩展的软件系统 其中包含单一职责原则(SRP),开闭原则(OCP),里氏替换原则(LSP),接口隔离原则(ISP), ...

  10. WPF入门教程系列目录

    WPF入门教程系列一--基础 WPF入门教程系列二--Application介绍 WPF入门教程系列三--Application介绍(续) WPF入门教程系列四--Dispatcher介绍 WPF入门 ...