前言:

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

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

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

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

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

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

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

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

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

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 Core 中执行,你将得到以下结果:

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

总结:

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

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

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

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

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

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

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

希望这个入门教程能够帮助你更好地理解.NET Emit,并为你的编程之路增添新的技能和知识!

.NET Emit 入门教程:第三部分:构建模块(Module)的更多相关文章

  1. Node.js入门教程 第三篇 (模块及路由)

    Node.js的模块 Node.js的模块与传统面向对象的类(class)不完全相同.Node.js认为文件即模块,即一个文件是一个模块.单一文件一般只专注做一件事情,保证了代码的简洁性. 创建模块: ...

  2. Docker入门教程(三)Dockerfile

    Docker入门教程(三)Dockerfile [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第三篇,介绍了Dockerfile的语法,DockerOn ...

  3. WCF入门教程(三)定义服务协定--属性标签

    WCF入门教程(三)定义服务协定--属性标签 属性标签,成为定义协议的主要方式.先将最简单的标签进行简单介绍,以了解他们的功能以及使用规则. 服务协定标识,标识哪些接口是服务协定,哪些操作时服务协定的 ...

  4. SQLite 入门教程(三)好多约束 Constraints(转)

    转于: SQLite 入门教程(三)好多约束 Constraints 一.约束 Constraints 在上一篇随笔的结尾,我提到了约束, 但是在那里我把它翻译成了限定符,不太准确,这里先更正一下,应 ...

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

    这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) GitHub 代码同步更新:ht ...

  6. Photoshop入门教程(三):图层

    学习心得:图层可以说是Photoshop的核心,看似简单,但是对于图像的各种编辑都是基于图层.他就像一层透明的.没有厚度的玻璃纸,每张玻璃纸设置不同的效果,层层叠加,最后显现出绚烂的效果. 在进行图像 ...

  7. Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导

    Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导 Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导 必要的message类实现 从下面开始是在veins/src/vei ...

  8. [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设

    [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设 敲黑板!! <q>元素添加短引用,<blockquote>添加长引用 在段落里添加引用就使用< ...

  9. WPF入门教程系列三——Application介绍(续)

    接上文WPF入门教程系列二——Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...

  10. C#入门教程(三)–接收用户输入、转义字符、类型转换-打造C#入门教程

    上次教程主要讲解了visual stdio快捷键.变量相关的知识.具体教程戳这里:http://www.chengxiaoxiao.com/net/1027.html 越来越深入去写教程越来越发现,自 ...

随机推荐

  1. AsyncHttpClient And Download Speed Limit

    AsyncHttpClient Official repository and docs: https://github.com/AsyncHttpClient/async-http-client M ...

  2. Laravel入坑指南(11)——列队

    很高兴,我们来到了Laravel入坑指南的第11篇.这一系列的文章已经接近尾声了,在这一节里面,我们一起讨论列队的用法. 列队,顾名思义,将需要处理的任务一个一个排好队,等待处理程序来处理.这机的列队 ...

  3. eclipse项目右击找不到build path

    右击项目–>properties–>Project Facets–>勾选右侧的Java,然后保存. 此时再操作就有了.

  4. SpringCloud服务注册与发现Eureka实战

    介绍 Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务 ...

  5. 马上就要元宵节了,这里给大家用css端上一碗汤圆

    「更多福利资讯查看:2024 首次大厂挑战」. ` .... ` .bowl_wrap{ width: 200px; height: 220px; margin: 100px auto; positi ...

  6. 万字Java进阶笔记总结

    JavaApi 字符串 String 注意:Java中"=="操作符的作用: 基本数据类型:比较的是内容. 引用数据类型比较的是对象的内存地址. StringBuffer/Stri ...

  7. 【Azure Redis 缓存】Lettuce 连接到Azure Redis服务,出现15分钟Timeout问题

    问题描述 在Java应用中,使用 Lettuce 作为客户端SDK与Azure Redis 服务连接,当遇见连接断开后,长达15分钟才会重连.导致应用在长达15分的时间,持续报错Timeout 问题解 ...

  8. 【Azure 应用服务】VS2019发布应用到正在运行的App Service时失败问题的解决

    问题描述 在VS 2019中配置号App Service的Publish Profile后,发布应用出现错误.根据VS 2019中的输出消息可知有文件正在运行中,无法被替换,所以发布失败. 问题解决 ...

  9. Taurus.MVC WebMVC 入门开发教程6:路由配置与路由映射

    前言: 在本篇 Taurus.MVC WebMVC 入门开发教程的第六篇文章中, 我们将讨论如何配置路由并映射到控制器和操作方法. 路由是决定应用程序如何响应客户端请求的重要组成部分,因此在 Web ...

  10. D3.js 力导向图的显示优化(二)- 自定义功能

    摘要: 在本文中,我们将借助 D3.js 的灵活性这一优势,去新增一些 D3.js 本身并不支持但我们想要的一些常见的功能:Nebula Graph 图探索的删除节点和缩放功能. 文章首发于 Nebu ...