.NET Emit 入门教程:第六部分:IL 指令:6:详解 ILGenerator 指令方法:方法调用指令
前言:
经过前面几篇的学习,我们了解到指令的大概分类,如:
参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令。
参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变量中,以方便后续使用。
创建实例指令,其指令以 New 开头,用于在运行时动态生成并初始化对象。
本篇介绍方法调用指令,该指令以 Call 开头,用于在运行时调用其它方法。
方法调用指令介绍:
在.NET Emit 中,方法调用指令是一种关键的操作,它允许我们在运行时动态地调用各种方法。
这些指令提供了一种灵活的方式,可以在程序执行期间创建、修改和调用方法,从而实现了动态代码生成和操作的功能。
方法调用指令包括了一系列不同的操作码,每个操作码都代表了一种不同的调用方式,比如调用实例方法、静态方法或委托。
通过理解和应用这些方法调用指令,我们可以实现诸如动态代理、AOP(面向切面编程)、方法重写等高级功能,从而扩展了.NET平台的能力和灵活性。
在本文中,我们将深入探讨ILGenerator 指令方法中与方法调用相关的内容,包括不同调用指令的详细解释、示例和实践应用场景。
1、常用指令:Call 指令及 Callvirt 指令
以下是两种常见的方法调用指令及其详细说明:
Call 指令:
- 作用:用于调用静态方法、实例方法以及基类的虚拟方法。
- 使用方法:需要提供方法的签名和目标对象(如果是实例方法)。
- 示例:
// 调用静态方法
IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); // 调用实例方法
IL.Emit(OpCodes.Call, typeof(MyClass).GetMethod("InstanceMethod"));
Callvirt 指令:
- 作用:用于调用虚方法,会在运行时根据对象的实际类型进行分派。
- 使用方法:需要提供方法的签名,调用时会自动获取对象的类型。
- 示例:
// 调用虚方法
IL.Emit(OpCodes.Callvirt, typeof(BaseClass).GetMethod("VirtualMethod"));
这些指令提供了灵活的方法调用功能,可以在动态生成的代码中使用,也可以用于实现诸如反射、AOP等功能。
通过深入理解这些指令的工作原理和使用方法,我们可以更加灵活地操作.NET平台上的方法调用行为。
2、Call 指令和 Callvirt 指令的区别:
在面向对象的编程语言中,"Call" 和 "CallVirt" 通常用于描述方法(函数)的调用方式,它们之间的区别在于是否进行虚拟方法调用(Virtual Method Invocation)。
Call(直接调用):当使用 "Call" 调用方法时,编译器会在编译时确定要调用的方法,这意味着它会直接调用指定类的方法,而不考虑实际运行时对象的类型。这种方式通常用于非虚方法(non-virtual method)或静态方法(static method),因为这些方法在编译时就已经确定了调用的目标。
CallVirt(虚拟方法调用):而当使用 "CallVirt" 调用方法时,编译器会生成一段代码,在运行时根据实际对象的类型来确定要调用的方法。这意味着即使在编译时使用的是基类的引用或指针,但在运行时实际上调用的是子类的方法(如果子类重写了该方法)。这种方式通常用于虚方法(virtual method),以实现多态性(polymorphism)。
总的来说,"Call" 是在编译时确定调用的方法,而 "CallVirt" 则是在运行时根据对象的实际类型确定调用的方法,从而实现了多态性。
使用及其性能说明:
在多数实例方法的调用,使用 Call 方法调用,会有更优的性能(实例方法时:它减少了对象的Null检查与虚方法重写的寻找)。
3、辅助方法:EmitCall
看一下说明:

从参数的说明中,可以看出,它提供了基于Call、Callvirt、Newobj 三类指令的封装调用。
在使用过程中,对使用者容易造成混乱,代码也不美观,可以无视它。
4、方法指针(委托)调用:Calli 指令
在C#语法中,除了 unsafe 方法可以操作指针外,其它涉及指针(引用地址)的被封装后提供给使用的安全类型只有 ref、out、委托。
而涉及调用的只有委托,因此,下面来一个调用委托的示例代码:
单独的使用 Emit 的Calli 指令无法直接调用委托方法,我们需要使用它封装的辅助方法来使用。
看一下说明:

该方法提供基于 Calli 指令的封装,提供针对委托的调用,下面看一组示例代码。
调用示例:
public static void PrintHello()
{
Console.WriteLine("Hello, world!");
} //...... ILGenerator il = methodBuilder.GetILGenerator(); // 加载一个委托实例到栈上
il.Emit(OpCodes.Ldftn, typeof(AssMethodIL_Call).GetMethod("PrintHello"));
// 使用 Calli 指令调用委托所指向的方法
il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(void), null, null);
il.Emit(OpCodes.Ret); // 返回该值
生成的对照代码:

有点偏离我们理解的代码了,好在它能正常执行。
我们在动态方法中运行它:

说明:
Ldftn 指令:Load Function 的简写,加载方法的引用地址。
总结:
本文探讨了.NET Emit 入门教程的第六部分,聚焦于ILGenerator中的方法调用指令。
通过详细分析 ILGenerator 的使用方法和方法调用指令,读者可以更深入地了解.NET平台下动态生成代码的实现机制。
通过本文的阅读,读者可以更加熟练地使用 ILGenerator 来动态生成高效、灵活的代码,为.NET应用程序的开发和优化提供更多可能性。
下一篇,我们继续探讨其它 IL 指令。
.NET Emit 入门教程:第六部分:IL 指令:6:详解 ILGenerator 指令方法:方法调用指令的更多相关文章
- Golang入门教程(十三)延迟函数defer详解
前言 大家都知道go语言的defer功能很强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在 ...
- Docker入门教程(六)另外的15个Docker命令
Docker入门教程(六)另外的15个Docker命令 [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第六篇,继续介绍Docker命令.之前的第二篇文章 ...
- 无废话ExtJs 入门教程十六[页面布局:Layout]
无废话ExtJs 入门教程十六[页面布局:Layout] extjs技术交流,欢迎加群(201926085) 首先解释什么是布局: 来自百度词典的官方解释:◎ 布局 bùjú: [distributi ...
- Photoshop入门教程(六):通道
学习心得:当大部分人听到通道.心里可能会有一种很害怕的感觉,因为“通道”并不像“图层”这样易于理解,望而生畏.”通道“的本质其实是存储图片的信息,把一张图片比作一个为网站,那么通道就是网站的后台,存储 ...
- RabbitMQ入门教程(十六):RabbitMQ与Spring集成
原文:RabbitMQ入门教程(十六):RabbitMQ与Spring集成 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...
- ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解
原文 ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 ...
- SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解
SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解 博客分类: 跟开涛学SpringMVC 6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于 ...
- SaltStack 入门到精通第二篇:Salt-master配置文件详解
SaltStack 入门到精通第二篇:Salt-master配置文件详解 转自(coocla):http://blog.coocla.org/301.html 原本想要重新翻译salt-mas ...
- 【入门】广电行业DNS、DHCP解决方案详解(三)——DNS部署架构及案
[入门]广电行业DNS.DHCP解决方案详解(三)——DNS部署架构及案 DNS系统部署架构 宽带业务DNS架构 互动业务DNS架构 案例介绍 案例一 案例二 本篇我们将先介绍DNS系统部署架构体系, ...
- kafka实战教程(python操作kafka),kafka配置文件详解
kafka实战教程(python操作kafka),kafka配置文件详解 应用往Kafka写数据的原因有很多:用户行为分析.日志存储.异步通信等.多样化的使用场景带来了多样化的需求:消息是否能丢失?是 ...
随机推荐
- 【Azure 应用服务】VS2019发布应用到正在运行的App Service时失败问题的解决
问题描述 在VS 2019中配置号App Service的Publish Profile后,发布应用出现错误.根据VS 2019中的输出消息可知有文件正在运行中,无法被替换,所以发布失败. 问题解决 ...
- 使用JMeter的JSON提取器:通过递归下降查找,从接口响应中提取特定字段
在接口测试中,我们经常需要从返回的JSON数据中提取特定字段以便后续使用.JMeter提供了JSON提取器,可以帮助我们实现这一目标.本文将介绍如何使用JMeter的JSON提取器通过递归下降查找的方 ...
- 影刀rpa:第二个项目学习心得
教程有说到元素的关联操作,教程说自上而下的html路径,一时之间没弄清楚,索性就去看了下网页的html源码,才弄清楚到底是咋回事: 我是先选中了列表子元素的价格字段,选择两次以后就能选择到所有列表子元 ...
- UI自动化如何保持登录状态?
给页面设置cookie信息,刷新页面清楚缓存实现保持登录状态: 通过cookie保持登录,1手动登录,通过applacation提取登录的cookie,2通过driver.add_cookie({&q ...
- 英语单词组件- 单词在句子中,上面显示中文下面显示音标 css样式
原先效果: 改进demo效果 优化点 音标长度超出,或者中文超出,总宽度会按照最长的走 居中显示 再次优化 line-height: 22px; 加入这个 对齐中间行(字号大小会让绝对上下高度,对不齐 ...
- .vscode\settings.json .gitignore 项目文件配置
一.项目本地新增配置文件: .vscode\settings.json { // editor "editor.formatOnSave": true, "e ...
- Android 设置任意View的背景为圆形或圆角
原文地址: Android 设置任意View的背景为圆形或圆角 - Stars-One的杂货小窝 我的需求主要还是在SurfaceView需要设置为圆形背景,在网上找了下,发现一个可行方法,做个记录 ...
- 重塑元宇宙体验!3DCAT元宇宙实时云渲染解决方案来了
元宇宙作为人工智能.云计算和数字孪生等前沿技术的结合体,近年来越发受到各大企业重视. 元宇宙的应用场景层出不穷,不仅包括营销推广场景,还有品牌活动和电商销售,能有效提升品宣和商业转化效果. 元宇宙也具 ...
- 瑞云科技荣获全国电子信息行业专精特新“最具创新价值 TOP20”!
"专精特新",从概念提出到写入政府工作报告走过了十年.这十年来我国促进中小企业发展力度之大.出台政策之密集.含金量之高前所未有,足见走专精特新发展之路深入人心."专精特新 ...
- C#中的For与Foreach循环:一场性能对话与实战解析
引言 在C#编程实践中,选择适当的循环结构对程序性能至关重要,尤其是在处理大量数据或追求极致运行效率时.本文将深入探讨C#中的两种主要迭代机制--传统的for循环和基于集合迭代器的foreach循环之 ...