.NET Emit 入门教程:第四部分:构建类型(Type)
前言:
在动态生成代码的过程中,构建类型(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)的更多相关文章
- Docker入门教程(四)Docker Registry
Docker入门教程(四)Docker Registry [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第四篇,介绍了Docker Registry,它 ...
- WCF入门教程(四)通过Host代码方式来承载服务
WCF入门教程(四)通过Host代码方式来承载服务 之前已经讲过WCF对外发布服务的具体方式. WCF入门教程(一)简介 Host承载,可以是web,也可以是控制台程序等等.比WebService有更 ...
- 无废话ExtJs 入门教程十四[文本编辑器:Editor]
无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功 ...
- SQLite 入门教程(四)增删改查,有讲究 (转)
转于: SQLite 入门教程(四)增删改查,有讲究 一.插入数据 INSERT INTO 表(列...) VALUES(值...) 根据前面几篇的内容,我们可以很轻送的创建一个数据表,并向其中插入一 ...
- 【知识整理】这可能是最好的RxJava 2.x 入门教程(四)
这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) 这可能是最好的RxJava 2. ...
- 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代码方式来 ...
- Photoshop入门教程(四):混合模式
学习心得:混合模式在Photoshop常容易被忽视,最大原因就是它所处的位置比较隐蔽,在图层面板左上部的角落里.使用混合模式,决定图像中上图层像素如何与图像中的下层像素进行混合,使图层的叠加更加炫酷. ...
- Veins(车载通信仿真框架)入门教程(四)——调试及记录结果
Veins(车载通信仿真框架)入门教程(四)——调试及记录结果 在Veins入门教程(三)最后的动图中(如下图)可以看到大大小小的光圈,这个怎么实现的呢? 很简单,以收到RTS消息为例,通过finHo ...
- RabbitMQ入门教程(十四):RabbitMQ单机集群搭建
原文:RabbitMQ入门教程(十四):RabbitMQ单机集群搭建 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://b ...
- GAN网络之入门教程(四)之基于DCGAN动漫头像生成
目录 使用前准备 数据集 定义参数 构建网络 构建G网络 构建D网络 构建GAN网络 关于GAN的小trick 训练 总结 参考 这一篇博客以代码为主,主要是来介绍如果使用keras构建一个DCGAN ...
随机推荐
- 蔚来杯2022牛客暑期多校训练营2 GJK
比赛链接 G 题解 知识点:思维. \(lds(p)\) 表示最小上升子序列分划数 (Dilworth 定理) \(lis(p)\cdot lds(p) \geq n \Rightarrow max ...
- java 如何实现开箱即用的敏感词控台服务?
sensitive-word-admin sensitive-word-admin 是基于 sensitive-word 实现的, 一款开箱即用的敏感词控台服务. 特性 基本的 CRUD 开箱即用的配 ...
- 利用nssm将jar包安装为windows服务
1.介绍 假设我们有一个spring boot程序打包成jar文件放到windows服务器运行,第一种方式jar -jar xx方式在cmd运行.这样有个缺点,被别人误关闭了咋办?服务器重启了咋办? ...
- 解决主机ssh虚拟机linux慢的问题
1.编辑sshd配置文件: vi /etc/ssh/sshd_config 找到行:#UseDNS yes 将注释去掉,把yes改为no 2.重启sshd服务: service sshd restar ...
- eclipse解决maven不自动下载jar包问题
今天在eclipse中import一个maven项目,由于我手工中断了相关pom.xml中配置的jar包下载. 导致项目找不到一些jar包.具体错误见下图: 原因就是有些jar包下载没有完成导致的. ...
- Golang从入门到跑路-从基础到微服务学习路线图
收录的awesome-go项目,学习基础系列,go项目实战,go源码分析,go开发者成长路线图等等,把他们收集起来学习. 地址:https://github.com/jiujuan/go-collec ...
- 项目实战:Qt管道焊接参数条码打印系统(条码打印机TSC 244 Pro、打印条码、打印中文、打印字符、多张连续打印)
需求 电脑端通过条码打印机TSC-TTP244 Pro: 1. 打印出尺寸为60*30cm 2. 条码打印机TSC-TTP244 Pro 不干胶纸 (不需要碳带,热敏纸) 3. window ...
- gorm入门学习
文档 https://learnku.com/docs/gorm/v2/v2_release_note/9756 https://gorm.cn/zh_CN/docs/ 下载安装 go get -u ...
- 【Filament】Filament环境搭建
1 前言 Filament 是一个实时物理渲染引擎,用于 Android.iOS.Linux.macOS.Windows 和 WebGL 平台.该引擎旨在提供高效.实时的图形渲染,并被设计为在 A ...
- 如何在矩池云复现开源对话语言模型 ChatGLM
ChatGLM-6B 是一个开源的.支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数.结合模型量化技术,用户可以在消费级的显卡上进行 ...