.NET Emit 入门教程:第五部分:动态生成方法(MethodBuilder 与 DynamicMethod)
前言:
当我们涉及到在运行时生成和定义方法时,便需要使用到C#中的两个关键类之一:MethodBuilder 或 DynamicMethod。
这两者都属于反射(Reflection.Emit)的一部分,允许我们以动态的方式创建方法。
两者各有侧重,使用方式大体相同,本篇文章我们先介绍 MethodBuilder,再介绍 DynamicMethod,最后再总结两者的区别。
1、MethodBuilder 介绍:
MethodBuilder
是一个强大的工具,用于在动态程序集中创建方法。
如果你需要构建整个类型(包括字段、属性、方法等),那么按流程:
首先你需要创建一个动态程序集(AssemblyBuilder
),
然后在其中创建一个模块(ModuleBuilder
),
最后再创建一个或多个类型(TypeBuilder
)。
而要在这些类型中创建方法,就可以使用 MethodBuilder
。
其关键特点:
- 绑定到类型:
MethodBuilder
创建的方法是属于某个类型的一部分,因此只能通过该类型的实例或静态引用来调用。 - 方法签名:需要指定方法名称、参数类型和返回类型。
- IL代码生成:需要手动编写IL代码。
2、MethodBuilder 代码:定义方法
正如上文所说,MethodBuilder 的使用,需要定义整个程序集上下文。
因此我们先编写一下共用部分代码,同样用于( .NET 版本生成程序集,以便对照生成代码):
AssemblyName assName = new AssemblyName("myAssembly") { Version = new Version("1.1.1.2") };
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("myModule", "b.dll");
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class); //定义方法...... tb.CreateType();
ab.Save("b.dll");
方法定义的过程:
1、通过 TypeBuilder 的 DefineMethod 来定义方法:MethodBuilder methodBuilder = tb.DefineMethod("方法名",......);
2、通过构造函数,可以设定方法定义的参数;
3、也可以后面再通过 MethodBuilder 实例 的 SetXXX 及 系列来定义参数。
下面示例展示方法的定义:
A、定义实例方法:使用简单参数
//定义实例方法:通过构造函数指定方法修饰符:Public、返回值:typeof(string)、参数类型:typeof(int),typeof(string)
MethodBuilder methodBuilder = tb.DefineMethod("MyMethod", MethodAttributes.Public, typeof(string), new Type[] { typeof(int), typeof(string) });
//定义参数的名称:指定参数名称:id,name
methodBuilder.DefineParameter(1, ParameterAttributes.None, "id");
methodBuilder.DefineParameter(2, ParameterAttributes.None, "name");
//用IL编写方法实现
var il = methodBuilder.GetILGenerator();
il.EmitWriteLine("hello world!");
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
对照生成的代码:
注意事项:
1:DefineParameter 对参数的索引从1开始(对实例方法或静态方法都一样)。
2:IL 构建代码时:实例方法下 Ldarg_0 是 this 自身,静态方法下 Ldarg_0 是id参数。
B、定义静态方法:参数进阶【包括ref、out、指针】大杂绘
//定义静态方法
MethodBuilder methodBuilder = tb.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(object), new Type[] { typeof(int).MakeByRefType(), typeof(string).MakePointerType() });
methodBuilder.DefineParameter(1, ParameterAttributes.None, "id");
methodBuilder.DefineParameter(2, ParameterAttributes.Out | ParameterAttributes.Optional, "name"); var il = methodBuilder.GetILGenerator();
il.EmitWriteLine("hello world!");
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
对照生成的代码:
简要说明:
1、ref 参数定义:在构造函数类型用:typeof(int).MakeByRefType()
2、指针 参数定义:在构造函数类型用:typeof(int).MakePointerType()
3、out 参数定义:在 DefineParameter 方法的 ParameterAttributes.Out 参数指定。
注意事项:在静态方法中,IL 代码 Ldarg_0 指向第一个参数 id。
C、定义方法:添加特性
// 添加自定义特性
ConstructorInfo attributeConstructor = typeof(AssemblyTitleAttribute).GetConstructor(new Type[] { typeof(string) });
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeConstructor, new object[] { "ExampleAttribute" });
methodBuilder.SetCustomAttribute(attributeBuilder);
对照生成的代码:
3、MethodBuilder 代码:方法的动态调用
对于运行时生成的动态方法实现动态调用,可以通过反射调用。
下面演示(将方法变更为实例方法,使用反射调用):
通过获取类的定义Type classType,然后Type.GetMethod获取方法进行调用,并传进创建的实例作为参数之一。
当然,如果方法调用频率很高,要更进一步,也可以为 MethodInfo 创建委托,来实现更高效率的调用。
然而:
Delegate.CreateDelegate(......) 只支持从静态方法转委托方法,不支持实例方法。 所以: 默认是不能实现对实例 MethodInfo 转委托的高效调用的。
但,那是默认,经过本人的百日创新,结合 DynamicMethod,还是有办法实现的。
实现方式可以参考本人的开源框架 Taurus.MVC 中关于 Deletgate 的相关实现。
既然提到了 DynamicMethod,那下面就开始介绍它了。
4、DynamicMethod 介绍:
DynamicMethod
则更加灵活。它允许在运行时生成和执行方法,而无需创建动态程序集或动态类型来容纳该方法。
这意味着你可以直接生成和执行少量代码,而不必担心整个类型的构建。
关键特点:
- 不绑定到类型:
DynamicMethod
不属于特定的类型,因此可以在任何上下文中调用。 - 性能:通常比
MethodBuilder
更高效。
5、DynamicMethod 代码:
//创建动态方法
DynamicMethod dynamicMethod = new DynamicMethod("MyMethod", typeof(void), null);
var il = dynamicMethod.GetILGenerator();
il.EmitWriteLine("hello world!");
il.Emit(OpCodes.Ret); //创建调用委托
var deletegateMethod = dynamicMethod.CreateDelegate(typeof(Action)) as Action;
//执行委托
deletegateMethod();
执行结果:
简要说明:
1、DynamicMethod 拥有的基础定义方法和 MethodBuilder 基本一致。
2、DynamicMethod 动态方法关注点是运行时执行,因此,可以省略很多参数的定义,只需要定义最简单的方法名、返回值、输入参数。
3、DynamicMethod 默认创建的静态方法,因此,它可以默认拥有 CreateDelegate 来实现高效调用。
6、DynamicMethod 与 MethodBuilder 两者的区别:
在C#中,MethodBuilder
和DynamicMethod
都属于反射发射(Reflection.Emit)的一部分,用于在运行时生成和定义方法。
让我们来简要介绍一下它们之间的区别:
DynamicMethod:
DynamicMethod
类允许在运行时生成和执行方法,而无需创建动态程序集或动态类型来容纳该方法。- 由即时(JIT)编译器生成的可执行代码在
DynamicMethod
对象被回收时也会被回收。 - 动态方法是生成和执行少量代码的最有效方式。
- 如果需要动态创建一个或多个方法,应使用
DynamicMethod
。
MethodBuilder:
MethodBuilder
用于在动态程序集中创建方法。- 如果要创建整个类型(包括字段、属性、方法等),则需要先创建一个动态程序集(
AssemblyBuilder
),然后在其中创建一个模块(ModuleBuilder
),最后再创建一个或多个类型(TypeBuilder
)。 - 若要在这些类型中创建方法,可以使用
MethodBuilder
。
通俗的讲人话即是:
1、如果是生成动态程序集,包括创建动态类,那么使用 MethodBuilder。 2、如果只是定义动态方法供调用,使用 DynamicMethod,因为它不用定义整个程序集,直接起手就是方法。 3、使用委托调用方法:MethodBuilder 和 DynamicMethod 都支持,但 DynamicMethod 直接提供 CreateDelegate 方法,方便起手调用。
总结:
在本文的第五部分中,我们深入探讨了 .NET Emit 中动态生成方法的两种方式:MethodBuilder 和 DynamicMethod。
通过 MethodBuilder,我们可以在运行时动态创建和定义方法,为其添加参数、自定义属性等元数据信息。
而 DynamicMethod 则提供了一种更灵活的动态方法生成方式,特别适合于需要高性能的动态代码生成场景。
在本篇教程中,我们学习了如何使用 MethodBuilder 来创建动态方法,并且演示了如何定义带有引用参数的动态方法以及如何向动态方法添加自定义属性。
这些内容对于理解动态方法的创建和扩展具有重要意义。
在下一篇中,我们将重点讲述IL语言,IL(Intermediate Language)是.NET平台上的一种中间语言,它是由C#、VB.NET等高级语言编译成的一种低级语言表示形式。
我们将会详细介绍IL语言的基本结构、指令集、堆栈操作等内容,帮助读者更深入地理解.NET动态方法的内部实现和执行过程。
通过深入了解IL语言,读者将能够更好地掌握.NET平台上动态代码生成的技术,并且能够对IL代码进行优化和调试,从而更好地应用于实际的软件开发项目中。
敬请期待下一篇教程的发布,让我们一起探究IL语言的奥秘吧!
.NET Emit 入门教程:第五部分:动态生成方法(MethodBuilder 与 DynamicMethod)的更多相关文章
- WCF入门教程(五)配置文件
WCF入门教程(五)配置文件 服务协定以及实现写好后,需要将相关服务公布出去,就需要HOST来承载,供客户端来调用. 承载服务有两种方式,一种通过配置文件,一种通过代码进行配置.上一章已经介绍了代码方 ...
- Docker入门教程(五)Docker安全
Docker入门教程(五)Docker安全 [编者的话]DockOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第五篇,介绍了Docker的安全问题,依然是老话重谈,入门者可以通 ...
- 无废话ExtJs 入门教程十五[员工信息表Demo:AddUser]
无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一 ...
- esri-leaflet入门教程(5)- 动态要素加载
esri-leaflet入门教程(5)- 动态要素加载 by 李远祥 在上一章节中已经说明了esr-leaflet是如何加载ArcGIS Server提供的各种服务,这些都是服务本身来决定的,API脚 ...
- 【知识整理】这可能是最好的RxJava 2.x 入门教程(五)
这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) 这可能是最好的RxJava 2. ...
- Photoshop入门教程(五):滤镜
学习心得:滤镜通常用于摄影行业,是安装在相机镜头前用于过滤自然光的附加镜头,从而获得一些特殊的效果.同理,Photoshop的滤镜也是为了产生特殊的效果.Photoshop滤镜分为两类:一种是内部滤镜 ...
- RabbitMQ入门教程(十五):普通集群和镜像集群
原文:RabbitMQ入门教程(十五):普通集群和镜像集群 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.c ...
- 【Spring Framework】Spring入门教程(五)AOP思想和动态代理
本文主要讲解内容如下: Spring的核心之一 - AOP思想 (1) 代理模式- 动态代理 ① JDK的动态代理 (Java官方) ② CGLIB 第三方代理 AOP概述 什么是AOP(面向切面编程 ...
- DQN(Deep Q-learning)入门教程(五)之DQN介绍
简介 DQN--Deep Q-learning.在上一篇博客DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird 中,我们使用Q-Table来 ...
- 2016 版 Laravel 系列入门教程(五)【最适合中国人的 Laravel 教程】
本教程示例代码见: https://github.com/johnlui/Learn-Laravel-5 在任何地方卡住,最快的办法就是去看示例代码. 本文是本系列教程的完结篇,我们将一起给 Arti ...
随机推荐
- NC15077 造一造
题目链接 题目 题目描述 WYF正试图用一个栈来构造一棵树,现在他已经构造了n个元素作为树的节点,只要将这n个元素依次入栈出栈就可以形成一棵树了.当然,这个问题与树并没有关系,所以它叫做WYF的栈.每 ...
- NVME(学习笔记四)—概念解读
1. 综述 NVMe over PCIe协议,定义了NVMe协议的使用范围.指令集.寄存器配置规范等. 名词解释 1.1.1 Namespace Namespace是一定数量逻辑块(LB)的集合,属性 ...
- STC89C52驱动MAX7219LED点阵级联, 文字滚动效果
级联下的传值方式 级联下, N个MAX7219相当于组成了一个8*N bit宽度的锁存器, 如果需要对第M个7219进行写入, 需要做M次寻址+写入后拉高CS, 才能到达这个7219. 如果仅仅对这个 ...
- wxPython 笔记
安装 Win7 / Win10 直接通过 pip install wxpython 安装 Ubuntu18.04 / Ubuntu 20.04 在Linux下的安装会稍微麻烦, 可以参考官网上的说明 ...
- 海康摄像SDK开发笔记(一):海康威视网络摄像头SDK介绍与模块功能
前言 视频监控.人脸识别等应用中经常使用到摄像头,当前占据主流视频监控摄像头就是海康和大华两家,都可通过自家的sdk或者是onvif方式使用和控制摄像头. 本文章讲解海康的sdk方式. 海康 ...
- Kotlin 基础入门
目录 一.基础语法 1.1 常见数据类型 1.2 变量 1.2.1 变量声明 1.2.2 类型推断 1.2.3 Null 安全 1.2.4 面向对象语言 1.3 流程控制 1.3.1 if 表达式 1 ...
- 深入解析Python并发编程的多线程和异步编程
本文分享自华为云社区<Python并发编程探秘:多线程与异步编程的深入解析>,作者:柠檬味拥抱. 在Python编程中,多线程是一种常用的并发编程方式,它可以有效地提高程序的执行效率,特别 ...
- [软件工程] CMMI是什么?
序 能力成熟度模型集成(CMMI) 一.CMMI(能力成熟度模型集成)概述 CMMI是由美国软件工程学会(software engineering institue,简称SEI)制定的一套专门针对软件 ...
- 扣子(coze.cn)| 由浅入深,手把手带你实现Java转型学习助手
扣子(coze.cn)是一款用来开发新一代 AI Chat Bot 的应用编辑平台,无论你是否有编程基础,都可以通过这个平台来快速创建各种类型的 Chat Bot,并将其发布到各类社交平台和通讯软件上 ...
- 浅析图数据库 Nebula Graph 数据导入工具——Spark Writer
从 Hadoop 说起 近年来随着大数据的兴起,分布式计算引擎层出不穷.Hadoop 是 Apache 开源组织的一个分布式计算开源框架,在很多大型网站上都已经得到了应用.Hadoop 的设计核心思想 ...