.NET Emit 入门教程:第三部分:构建模块(Module)

这篇具有很好参考价值的文章主要介绍了.NET Emit 入门教程:第三部分:构建模块(Module)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

在这一部分中,我们将深入讨论动态程序集中模块的概念以及如何构建和管理模块。

1、模块的概念:

模块是动态程序集中的基本单位,它类似于一个独立的代码单元,可以包含类型、方法、字段等成员。

在动态程序集中,模块扮演着组织代码和实现代码复用的关键角色。

它们允许开发人员将相关功能和数据组织在一起,并在需要时进行引用和重用。

一个程序集可以包含一个或多个模块,这种模块化的设计有助于提高代码的可维护性和可扩展性。

通俗的讲人话:

即在设计上:在运行时,一个程序集可以包含多个模块,每个模块允许用不同的语言编写,比如VB模块混合C#模块。

在使用上:在编绎后,一个程序集只能包含一个模块。

下面来看一个组问答题:

2、程序集和模块的关系问答:

既然一个程序集可以定义多个Module,为什么通过反编绎dll,发现所有的dll文件都只有一个module呢?

在使用 C# 或 .NET Framework 动态创建程序集和模块时,无论你创建了多个模块,最终生成的 DLL 文件通常只会包含一个默认模块。

这是因为在 .NET 中,一个程序集(assembly)通常对应一个 DLL 或者 EXE 文件,而每个程序集只包含一个默认的模块。 即使你在代码中使用 DefineDynamicModule 创建了多个模块,最终生成的 DLL 文件也只会包含默认模块的信息。

其他通过 DefineDynamicModule 创建的模块并不会以独立的形式出现在最终的 DLL 文件中。 实际上,在 .NET 中,程序集可以包含多个模块,但这些附加的模块一般不会直接保存在磁盘文件中,而是在运行时动态加载到程序集中。这也是为什么反编译时只能看到一个模块的原因。 总的来说,即使你在代码中动态创建了多个模块,最终生成的 DLL 文件也只会包含一个默认模块。其他动态创建的模块会以其他方式与程序集关联,并不会直接体现在生成的 DLL 文件中。

了解完程序集与模块的对应关系,下面看看如何创建动态模块:

3、创建动态模块:

使用C# Emit 技术可以在运行时动态创建模块。

首先,需要获得 AssemblyBuilder(这个在构建程序集一文中,已经讲解了 .NET 和 .NET Core 下的相关获取用法)。

然后,通过 AssemblyBuilder 的 DefineDynamicModule方法,即可获得 ModuleBuilder(后续章节会通过它,来添加类型、方法、字段等成员到模块中,从而构建出所需的模块结构)。

下面是一个获得 ModuleBuilder 示例代码:

 AssemblyBuilder ab = ......
 ModuleBuilder mb = ab.DefineDynamicModule("一个名称");

4、创建静态模块(仅.NET系列支持):

在.NET中,如果要将该模块持久化到 dll 中,则需要在重载中指定 dll 的名称,如:

 AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

//......

 ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("一个名称", dllName + ".dll");

//......
assemblyBuilder.Save(dllName
+ ".dll");

正如问答所说,一个程序集在持久化到文件中时,只能包含一个默认模块。

因此,通过指定和程序集相同的名称,来持久化到相同的程序集中。

如果定义多个模块,都指向同一个程序集名称中呢?

如下图,抛重复的文件名异常:

.NET Emit 入门教程:第三部分:构建模块(Module)

如果定义多个模块,都给予不同的程序集名称呢?

如下图,每个模块单独生成程序集:

.NET Emit 入门教程:第三部分:构建模块(Module)

因此,在运行时,可以定义多个模块,模块在运行时可以互动,但持久化到文件中,只能有一个模块。

5、模块间的交互

模块之间可能存在依赖关系,一个模块可能需要引用另一个模块中的类型或成员。

在动态程序集中,可以使用 AssemblyBuilder 来创建程序集并将模块进行组合,从而实现模块间的交互和依赖管理。

通常而言,不建议尝试在程序集中定义多个模块,下面给几个教训的示例:

因情节需要,以下内容中会提前出现 TypeBuilder、MethodBuilder 和 IL代码。

在 .NET 中定义多个模块,并尝试进行交互:

public static void Start()
{
    // 创建第一个模块
    AssemblyName assemblyName = new AssemblyName("MyAssembly");
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder1 = assemblyBuilder.DefineDynamicModule("Module1");

    // 在第一个模块中定义一个类型
    TypeBuilder typeBuilder1 = moduleBuilder1.DefineType("MyClass", TypeAttributes.Public);

    MethodBuilder methodBuilder1 = typeBuilder1.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), null);
    ILGenerator ilGenerator = methodBuilder1.GetILGenerator();
    ilGenerator.EmitWriteLine("Call hello from Module1!");
    ilGenerator.Emit(OpCodes.Ret);
    typeBuilder1.CreateType();


    // 在第二个模块中引用第一个模块中定义的类型
    MethodInfo myMethod = moduleBuilder1.GetType("MyClass").GetMethod("MyMethod");


    // 创建第二个模块
    ModuleBuilder moduleBuilder2 = assemblyBuilder.DefineDynamicModule("Module2");
    TypeBuilder typeBuilder2 = moduleBuilder2.DefineType("MyClass2", TypeAttributes.Public);
    MethodBuilder methodBuilder2 = typeBuilder2.DefineMethod("MyMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(void), null);
    var ilGenerator2 = methodBuilder2.GetILGenerator();
    ilGenerator2.EmitWriteLine("Exe hello from Module2!");
    ilGenerator2.EmitCall(OpCodes.Call, myMethod, null);
    ilGenerator2.Emit(OpCodes.Ret);
    typeBuilder2.CreateType();


    //assemblyBuilder.LoadModule()

    var myMethod2 = moduleBuilder2.GetType("MyClass2").GetMethod("MyMethod2");

    //myMethod.Invoke(null, null);
    myMethod2.Invoke(null, null);

    Console.Read();
}

结果如下:

.NET Emit 入门教程:第三部分:构建模块(Module)

 

同样的代码,在 .NET Core 中执行,你将得到以下结果:

.NET Emit 入门教程:第三部分:构建模块(Module)

所以,你懂的,别折腾这个。 

总结:

嗯,构建模块,一行代码的事情,愣是让我写成了一篇教程,太难了,下面进行总结。

在这个入门教程的第三部分中,我们学习了如何使用.NET Emit 构建模块(Module)。

通过创建和定义模块,我们可以更好地组织和管理我们的代码。

在这个过程中,我们了解了如何使用 AssemblyBuilder 和 ModuleBuilder 来动态生成模块。

通过学习构建模块的过程,我们可以更深入地理解.NET Emit 的强大功能,并且能够在运行时动态地生成和加载代码。

构建模块是.NET Emit中非常重要的一部分,它为我们提供了灵活性和扩展性,让我们能够更好地应对各种编程需求。

在接下来的学习中,我们将继续探索.NET Emit的各种功能和用法,不断丰富我们的动态代码生成技能,为我们的项目带来更多可能性。

希望这个入门教程能够帮助你更好地理解.NET Emit,并为你的编程之路增添新的技能和知识!文章来源地址https://www.toymoban.com/news/detail-842381.html

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

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

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

相关文章

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

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

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

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

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

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

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

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

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

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

    2024年01月17日
    浏览(51)
  • 【Git 入门教程】第三节、Git的分支和合并

    Git的分支和合并是Git中最重要的概念之一。使用Git可以轻松地创建、切换和合并分支,这为团队协作开发提供了极大的便利。在本文中,我们将介绍Git分支的基本概念和操作方式。 在Git中,分支是指一个代码库的不同版本。分支允许开发者独立地开发特定功能或修复故障,而

    2024年02月07日
    浏览(48)
  • odoo 开发入门教程系列-模块交互

    在上一章中,我们使用继承来修改模块的行为。在我们的房地产场景中,我们希望更进一步,能够为客户生成发票。Odoo提供了一个开发票模块,因此直接从我们的房地产模块创建发票是很简单的,也就是说,一旦某个房产设置为“已售出”,就会在 Invoicing 应用程序中创建发

    2023年04月16日
    浏览(39)
  • SpringBoot整合第三方技术 -- SpringBoot快速入门保姆级教程(三)

    为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。 (博客的参考源码可以在我主页的资源里找到,如果在学习的

    2024年02月09日
    浏览(43)
  • Python入门教程23:math模块的用法

    **math是Python 的一个内置模块,它提供了许多数学函数和常量,用于进行数学计算。**以下是一些常用的math模块中的函数和常量: math.pi:圆周率π的近似值,约等于3.14159。 math.e:自然对数的底数e的近似值,约等于2.71828。 math.sqrt(x):求平方根。 math.pow(x, y):求x的y次方。 ma

    2024年02月12日
    浏览(43)
  • Flink保姆级教程,超详细,教学集成多个第三方工具(从入门到精通)

    目录 一.Flink简介 Flink发展历程 Flink特性 二、Flink 部署及启动 1. 本地执行 (Local Execution) 2. Standalone 集群部署 2.1 会话模式(Session Mode) 2.2 单作业模式(Per-Job Mode)和应用模式(Application Mode) 3. 资源管理器集成部署 三.Flink架构和执行原理 Flink架构 任务槽和资源 Flink资源管

    2024年04月29日
    浏览(53)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包