前言:

在动态生成代码的过程中,构建类型(Type)是至关重要的一步。

通过使用 Emit 中的 TypeBuilder,我们可以定义和创建各种类型,包括类、结构体和接口。

本节将深入探讨如何使用 TypeBuilder 动态构建类型,并介绍其在实际应用中的重要性。

定义公用代码,生成程序集以供对照:

通过学习本系列之前的文章,我们可以轻松定义 AssemblyBuilder 程序集构建器,再通过程序集构建器,定义 ModuleBuilder 模块构建器。

下面我们先通过定义公用代码来生成程序集,以便更好的通过反编绎,来观察对照我们生成的代码。

AssemblyName assName = new AssemblyName("myAssembly");
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("myModule","a.dll"); //...今天的示例代码存放地 ab.Save("a.dll");

注意标红的部分为 .NET 版本代码,正如本系列之前文件所说,只有 .NET 版本支持程序集持久化,.NET Core 需要到9版本才支持。

.NET Core 用 AssemblyBuilder.DefineDynamicAssembly来构建。

ModuleBuilder 的几个定义方法:

1、定义枚举:

EnumBuilder eb=mb.DefineEnum("bbb", ...);

2、定义类(包括类、接口、结构体):

TypeBuilder tb=mbDefineType("aaa", ...);

3、定义内部类:

TypeBuilder tb=mbDefineType("aaa", ...);
TypeBuilder innerClassBuilder = tb.DefineNestedType("innerClass",...);

下面我们使用代码对照,来学习本节内容:

1、定义枚举:

EnumBuilder eb = mb.DefineEnum("MyNameSpace.MyEnum", TypeAttributes.Public, typeof(int));

eb.DefineLiteral("Spring", 0);
eb.DefineLiteral("Summer", 1);
eb.DefineLiteral("Autumn", 2);
eb.DefineLiteral("Winter", 3); Type enumType = eb.CreateType();

对应生成的代码:

2、定义接口:

 TypeBuilder tb = mb.DefineType("MyNameSpace.MyInterface", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Interface);

 //tb.DefineField("ID", typeof(int), FieldAttributes.Public| FieldAttributes.Static| FieldAttributes.InitOnly);

 // 定义属性 "Name",类型为 int
PropertyBuilder propertyBuilder = tb.DefineProperty("Name", PropertyAttributes.None, typeof(int), null); // 定义属性的 getter 方法
MethodBuilder getterMethodBuilder = tb.DefineMethod("get_Name", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(int), Type.EmptyTypes);
propertyBuilder.SetGetMethod(getterMethodBuilder); // 定义属性的 setter 方法
MethodBuilder setterMethodBuilder = tb.DefineMethod("set_Name", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, null, new Type[] { typeof(int) });
propertyBuilder.SetSetMethod(setterMethodBuilder);

//定义方法 GetMyName
MethodBuilder getMyName = tb.DefineMethod("GetMyName", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(string), new Type[] { typeof(int) }); tb.CreateType();

属性的定义,需要挂接 get_XXX 和 set_XXX 两个方法,会相对显的定义麻烦了点。

对应生成的代码:

3、定义结构体

// 定义结构体
TypeBuilder tb = mb.DefineType("MyNameSpace.MyStruct", TypeAttributes.SequentialLayout | TypeAttributes.Public | TypeAttributes.Sealed, typeof(ValueType)); // 定义字段
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineField("Name", typeof(string), FieldAttributes.Public); tb.CreateType();

对应生成的代码:

4、定义类:抽象类

 TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
tb.CreateType(); tb.CreateType();

MethodAttributes.Family 对应的即:protected 修饰符。

对应生成的代码:

5、定义类:并继承自抽象类:

//定义基类
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes); Type typeBase = tb.CreateType();

//定义子类,继承基类
TypeBuilder tbClass = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class, typeBase);
//实现抽象方法
MethodBuilder mbClass = tbClass.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
ILGenerator iL = mbClass.GetILGenerator();
iL.Emit(OpCodes.Ret);
tbClass.CreateType();

红色标注为指定继承,接口继承一样在该参数指定。

对应生成的代码:

6、定义类:增加泛型参数指定

//定义基类
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes); Type typeBase = tb.CreateType(); //定义子类继承基类
TypeBuilder tbClass = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class, typeBase);
//实现抽象方法
MethodBuilder mbClass = tbClass.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
ILGenerator iL = mbClass.GetILGenerator();
iL.Emit(OpCodes.Ret); // 定义泛型参数
string[] typeParamNames = { "T" };
GenericTypeParameterBuilder[] typeParams = tbClass.DefineGenericParameters(typeParamNames); //定义泛型方法
MethodBuilder methodBuilder = tbClass.DefineMethod("GetT", MethodAttributes.Public, typeParams[0], new Type[] { typeof(object) });
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ret); tbClass.CreateType();

这里通过定义泛型参数,来指定我们的泛型类。

对应生成的代码:

7、通过内部类定义委托:

// 定义内部类,并在内部类中定义委托类型
TypeBuilder delegateBuilder = tbClass.DefineNestedType("MyNameSpace.AuthDelegate", TypeAttributes.Class | TypeAttributes.NestedPublic | TypeAttributes.Sealed, typeof(MulticastDelegate)); // 添加委托的构造函数
ConstructorBuilder constructor = delegateBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); // 添加Invoke方法
delegateBuilder.DefineMethod("Invoke", MethodAttributes.Public, typeof(bool), new Type[] { typeof(string) }).SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); // 创建内部类和委托类型
Type authDelegateType = delegateBuilder.CreateType();

注意,这里是通过Type的形式,来定义委托。

因此,我们对其限定名称空间,限定其使用范围:

同时将委托定义在某个类当成员变量:

通过定义事件,是使用委托的方式之一。

8、定义事件:

//定义事件
EventBuilder eb = tbClass.DefineEvent("MyEvent", EventAttributes.None, delegateBuilder); MethodBuilder addMethod = tbClass.DefineMethod("add_OnAuth", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(void), new Type[] { delegateBuilder });
ILGenerator addMethodIL = addMethod.GetILGenerator();
//......
addMethodIL.Emit(OpCodes.Ret);
eb.SetAddOnMethod(addMethod); MethodBuilder removeMethod = tbClass.DefineMethod("remove_OnAuth", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(void), new Type[] { delegateBuilder });
ILGenerator removeMethodIL = removeMethod.GetILGenerator();
//......
removeMethodIL.Emit(OpCodes.Ret);
eb.SetRemoveOnMethod(removeMethod);

注意事项:

1、定义事件,通过特殊方法: DefineEvent 来定义。

2、定义事件,第三个事件参数Type,需要传递 delegateBuilder ,则不是 delegateType,否则会报错:

3、定义事件,需要同时挂两个对应的添加和移除方法,否则,运行正常,但反编绎会报错:

4、定义方法,传递的委托类型,和注意事项2一致,需要传递 delegateBuilder,否则一样的错误信息。

下面查看正常情况下的反绎绎生成代码:

对委托和事件的定义,一个神奇的Bug:

通过反编绎 ILSpy 软件,可以看到已经定义成功了,但通过引用生成的程序集,即发现里面没有相关的委托或事件产生?

同时通过 VS2022 自带的反编绎【直接F12跳转】,里面也没有任何相关的委托或事件代码?

总结

构建类型是动态代码生成过程中的关键一环,通过灵活运用 TypeBuilder 和相关工具,

我们可以实现各种复杂类型的动态生成,为程序的灵活性和可扩展性提供有力支持。

总的来说,本章节通过演示如何使用 Emit 来动态创建类型,包括定义字段、方法、属性和事件等,

帮助读者理解如何在运行时生成和操作类型信息。

.NET Emit 入门教程:第四部分:构建类型(Type)的更多相关文章

  1. Docker入门教程(四)Docker Registry

    Docker入门教程(四)Docker Registry [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第四篇,介绍了Docker Registry,它 ...

  2. WCF入门教程(四)通过Host代码方式来承载服务

    WCF入门教程(四)通过Host代码方式来承载服务 之前已经讲过WCF对外发布服务的具体方式. WCF入门教程(一)简介 Host承载,可以是web,也可以是控制台程序等等.比WebService有更 ...

  3. 无废话ExtJs 入门教程十四[文本编辑器:Editor]

    无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功 ...

  4. SQLite 入门教程(四)增删改查,有讲究 (转)

    转于: SQLite 入门教程(四)增删改查,有讲究 一.插入数据 INSERT INTO 表(列...) VALUES(值...) 根据前面几篇的内容,我们可以很轻送的创建一个数据表,并向其中插入一 ...

  5. 【知识整理】这可能是最好的RxJava 2.x 入门教程(四)

    这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) 这可能是最好的RxJava 2. ...

  6. WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]

    WCF入门教程(四)通过Host代码方式来承载服务 Posted on 2014-05-15 13:03 停留的风 阅读(7681) 评论(0) 编辑 收藏 WCF入门教程(四)通过Host代码方式来 ...

  7. Photoshop入门教程(四):混合模式

    学习心得:混合模式在Photoshop常容易被忽视,最大原因就是它所处的位置比较隐蔽,在图层面板左上部的角落里.使用混合模式,决定图像中上图层像素如何与图像中的下层像素进行混合,使图层的叠加更加炫酷. ...

  8. Veins(车载通信仿真框架)入门教程(四)——调试及记录结果

    Veins(车载通信仿真框架)入门教程(四)——调试及记录结果 在Veins入门教程(三)最后的动图中(如下图)可以看到大大小小的光圈,这个怎么实现的呢? 很简单,以收到RTS消息为例,通过finHo ...

  9. RabbitMQ入门教程(十四):RabbitMQ单机集群搭建

    原文:RabbitMQ入门教程(十四):RabbitMQ单机集群搭建 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://b ...

  10. GAN网络之入门教程(四)之基于DCGAN动漫头像生成

    目录 使用前准备 数据集 定义参数 构建网络 构建G网络 构建D网络 构建GAN网络 关于GAN的小trick 训练 总结 参考 这一篇博客以代码为主,主要是来介绍如果使用keras构建一个DCGAN ...

随机推荐

  1. NVME(学习笔记四)—概念解读

    1. 综述 NVMe over PCIe协议,定义了NVMe协议的使用范围.指令集.寄存器配置规范等. 名词解释 1.1.1 Namespace Namespace是一定数量逻辑块(LB)的集合,属性 ...

  2. .net core微服务之服务发现

    一:nacos https://nacos.io/docs/latest/what-is-nacos/ https://github.com/alibaba/nacos 二:consul https: ...

  3. 【framework】Task简介

    1 前言 ​ Task 即任务,一个 Task 对应一个 Activity,其父容器为 TaskStack,子容器为 AppWindowToken. ​ 关于其父类及祖父类的介绍,见 → Window ...

  4. PLSQL编译存储过程无响应

    解决方法如下: 1:查V$DB_OBJECT_CACHE SELECT * FROM V$DB_OBJECT_CACHE WHERE name='CRM_LASTCHGINFO_DAY' AND LO ...

  5. js与java使用AES加密算法实现前后端加密解密

    AES加密算法入门:https://blog.csdn.net/IndexMan/article/details/87284833 第三方crypto.js下载地址:https://download. ...

  6. Jsp+Servlet实现文件上传下载(四)--下载文件

    接着上一篇讲: Jsp+Servlet实现文件上传下载(三)--删除上传文件    点击打开链接 本章来实现一下下载文件功能,同时优化了一下上一章中的代码. 废话少说,上代码 ------------ ...

  7. vue 项目npm run dev(启动)时报错The service was stopped

    vue项目yarn upgrade后vite build报错,如何项目也运行不起来了. 报错截图: 解决办法: 删除node_modules文件夹,然后执行yarn install重新生成心的node ...

  8. 【Azure 应用服务】App Service / Function App 修改系统时区为中国时区的办法(Azure中所有服务的默认时间都为UTC时间,转换为北京时间需要+8小时)

    问题描述 在Azure的 App Service / Function App 服务中,如果是在Windows系统中,可以通过添加Application Setting来转换为中国时间(WEBSITE ...

  9. consul 的 HTTP API 和使用方法

    目录 搭建起 consul Consul 的 HTTP API Service API 简单地注册服务 健康检查的配置和查询 建议读者先学习笔者的另一篇文章 学习搭建 Consul 服务发现与服务网格 ...

  10. ffmpeg 使用记录

    这周周末尝试把我硬盘上面的视频文件压缩了一下,但是效果并不理想.其中主要有两个原因, 视频本来就是h264的编码,再重新编码也没啥用,因为限制大小的主要是码率 ffmpeg GPU加速版的h265编码 ...