前言:

在动态生成代码的过程中,构建类型(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. NC15532 Happy Running

    题目链接 题目 题目描述 Happy Running, an application for runners, is very popular in CHD. In order to lose wei ...

  2. NC22593 签到题

    题目链接 题目 题目描述 恭喜你找到了本场比赛的签到题! 为了让大家都有抽奖的机会,只需要复制粘贴以下代码(并且稍微填下空)即可 AC: (我超良心的) #include <algorithm& ...

  3. NC24608 [USACO 2011 Ope S]Learning Languages

    题目链接 题目 题目描述 Farmer John's N (2 <= N <= 10,000) cows, conveniently numbered 1..N, are fluent i ...

  4. 使用yum 报错 :This system is not registered with RHN

    解决办法:(假定你已安装yum,且网络畅通)更改yum的源, 即更换/etc/yum.repos.d/rhel-debuginfo.repo 这个文件.首先备份,如下所示: [root@localho ...

  5. win32-制作mini dump文件

    一个完整的用户模式dump是基本的用户模式转储文件. 此转储文件包括进程的整个内存空间,程序的可执行映像本身,句柄表以及其他信息,这些信息对于调试器在重建转储发生时正在使用的内存中很有用. 可以将完整 ...

  6. 商店销售预测(回归&随机森林)

    ​ 目录 一.题目概要 二.导入包和数据集 三.数据处理 四.描述性分析 五.探索性数据分析 六.模型一:线性回归 七.模型2:随机森林 一.题目概要 在Kaggle竞赛中,要求我们应用时间序列预测, ...

  7. portainer docker可视化工具

    下载可视化工具 docker pull portainer/portainer 启动portainer --restart=always 只要挂掉了 就自动重启 docker run -d -p 80 ...

  8. 制作docker方式执行Gitlab Runner所需要的镜像

    背景知识 启动Gitlab Runner时,使用Gitlab提供的官方镜像gitlab/gitlab-runner:latest即可. Runner以容器的方式启动以后,根据前文我们注册到Gitlab ...

  9. Java 异常处理(1) :try-catch-finally的使用

    1 package com.bytezero.throwable; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 impor ...

  10. Java 练习题(类+调用方法)

    1 /* 2 * 3 * 定义一个 PassObject,在类中定义一个方法printAress(),该方法的定义如下: 4 * public void printAreas(Circle c,int ...