.NET Emit 入门教程:第四部分:构建类型(Type)

这篇具有很好参考价值的文章主要介绍了.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();

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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 两个方法,会相对显的定义麻烦了点。

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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();

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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 修饰符。

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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();

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

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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();

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

对应生成的代码:

.NET Emit 入门教程:第四部分:构建类型(Type)

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的形式,来定义委托。

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

.NET Emit 入门教程:第四部分:构建类型(Type)

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

.NET Emit 入门教程:第四部分:构建类型(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,否则会报错:

.NET Emit 入门教程:第四部分:构建类型(Type)

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

.NET Emit 入门教程:第四部分:构建类型(Type)

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

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

.NET Emit 入门教程:第四部分:构建类型(Type)

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

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

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

.NET Emit 入门教程:第四部分:构建类型(Type)

总结

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

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

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

帮助读者理解如何在运行时生成和操作类型信息。文章来源地址https://www.toymoban.com/news/detail-843245.html

到了这里,关于.NET Emit 入门教程:第四部分:构建类型(Type)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .NET Emit 入门教程:第六部分:IL 指令:4:详解 ILGenerator 指令方法:参数存储指令

    上一篇介绍了 IL 指令的分类以及参数加载指令,该加载指令以ld开头,将参数加载到栈中,以便于后续执行操作命令。 本篇开始介绍参数存储指令,其指令以st开头,将栈中的数据,存储到指定的变量中,以方便后续使用。 在 IL 中,除了参数存储指令 starg 和 stloc 之外,还

    2024年04月08日
    浏览(32)
  • .NET Emit 入门教程:第六部分:IL 指令:7:详解 ILGenerator 指令方法:分支条件指令

    经过前面几篇的学习,我们了解到指令的大概分类,如: 参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令。 参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变量中,以方便后续使用。 创建实例指令,其指令以 New 开头,

    2024年04月12日
    浏览(35)
  • .NET Emit 入门教程:第六部分:IL 指令:3:详解 ILGenerator 指令方法:参数加载指令

    在上一篇中,我们介绍了 ILGenerator 辅助方法。 本篇,将详细介绍指令方法,并详细介绍指令的相关用法。 在接下来的教程,关于IL指令部分,会将指令分为以下几个分类进行讲解: 下面开始介绍第一部分,参数加载指令: 参数加载指令用于在方法中加载参数到操作数栈中,

    2024年04月08日
    浏览(58)
  • .NET Emit 入门教程:第六部分:IL 指令:9:详解 ILGenerator 指令方法:运算操作指令(指令篇结束)

    经过前面几篇的学习,我们了解到指令的大概分类,如: 参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令。 参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变量中,以方便后续使用。 创建实例指令,其指令以 New 开头,

    2024年04月22日
    浏览(27)
  • C语言入门教程,C语言学习教程(第三部分:C语言变量和数据类型)二

    前面我们多次提到了字符串,字符串是多个字符的集合,它们由 \\\" \\\" 包围,例如 \\\"http://c.biancheng.net\\\" 、 \\\"C语言中文网\\\" 。字符串中的字符在内存中按照次序、紧挨着排列,整个字符串占用一块连续的内存。 当然,字符串也可以只包含一个字符,例如 \\\"A\\\" 、 \\\"6\\\" ;不过为了操作方

    2024年01月17日
    浏览(40)
  • 【Git 入门教程】第四节、Git冲突:如何解决版本控制的矛盾

    Git是目前最流行的版本控制系统之一,它为团队协作开发提供了方便和高效的方式。然而,在多人同时修改同一个文件时,可能会出现代码 冲突(conflict) ,导致代码无法正确合并。那么,如何解决Git冲突呢? 在多分支并行处理时,每一个分支可能是基于不同版本的主干分

    2024年02月05日
    浏览(48)
  • python教程 入门学习笔记 第4天 数据类型 获取数据类型 字符串拼接

    数据类型 1、能直接处理的基本数据类型有5个:整型、浮点型、字符串、布尔值、空 1)整型(int)=整数,例如0至9,-1至-9,100,-8180等,人数、年龄、页码、门牌号等 没有小数位的数字,是整型 2)浮点型(float)=小数,例如金额、身高、体重、距离、长度、π等 精确到小

    2024年02月14日
    浏览(39)
  • Python保姆级教程 数据类型—新手小白入门必看系列

    推荐使用压缩软件和杀毒软件 7 - zip 使用火绒 优点: 代码说明 没注释的代码 有注释的代码 不让解释器执行注释的那句话 单行注释快捷键:ctrl + ? \\\"\\\"\\\"\\\"\\\"\\\" (三个双引号) 185730213551 什么是变量:可以改变的量 计算机用来存贮数据的盒子,想用这个数据,就直接那盒子就好了

    2024年02月16日
    浏览(39)
  • JAVA包装类和基本数据类型------JAVA入门基础教程

    public class WrapperTest {     public static void main(String[] args)     {         int i1 = 10;         Integer i11 = new Integer(i1);         System.out.println(i11);         float f1 = 12.3F;         f1 = 32.2F;         Float ff1 = new Float(f1);         System.out.println(ff1);         String s1 = \\\"32.1\\\";         F

    2024年02月02日
    浏览(29)
  • C语言入门教程||C语言 头文件||C语言 强制类型转换

    头文件是扩展名为  .h  的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。 在程序中要使用头文件,需要使用 C 预处理指令  #include  来引用它。前面我们已经看过  stdio.h  头文件,它是编译

    2024年02月02日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包