首页 > 其他分享 >.NET Emit 入门教程:第三部分:构建模块(Module)

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

时间:2024-03-22 13:55:18浏览次数:22  
标签:DefineDynamicModule 代码 入门教程 程序 Module 模块 NET Emit

前言:

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

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,并为你的编程之路增添新的技能和知识!

标签:DefineDynamicModule,代码,入门教程,程序,Module,模块,NET,Emit
From: https://www.cnblogs.com/cyq1162/p/18086265

相关文章

  • Kinetic Tournament Tree
    考虑这样一个问题:\(n\)个一次函数\(k_ix_i+b_i\),每个一次函数初始有\(x_i=0\);区间对\(x_i\)加正数\(x\),区间查询\(\max\limits_{i=l}^rk_ix_i+b_i\)。考虑每个点维护当\(x_i=0\)时值最大的函数,然后额外维护一个阈值\(t\),表示当\(x\)增大到\(t\)时这个......
  • 启用 .NET Framework 3.5
    可以在启用.NETFramework3.5功能时,将Windows安装媒体用作文件源。为此,请按照下列步骤操作:插入Windows安装媒体。(https://zhuanlan.zhihu.com/p/616060244)在提升的命令提示符处,运行下面的命令:控制台复制 Dism/online/enable-feature/featurename:NetFx3/......
  • 超简单的.net Core上传文件到七牛云保存
    经过我一天的努力,在网上查找内容,外加看官网配置,再加请教前辈,终于总结出一个简单的方法来实现这个功能1、需要你注册七牛的账号,提交实名认证2、登录七牛云平台->对象存储->新建空间我用的是这个NuGet包,每个人都有每个人的方法,可以借鉴我的开发环境:操作系统:Windows10家庭中......
  • 如何在Kubernetes集群中集成Cromwell和Volcano(概述)
    将Cromwell和Volcano在Kubernetes集群中集成,使用Volcano作为Cromwell调度器,涉及到在Kubernetes集群上安装和配置这两个系统以及确保它们能够无缝协作。以下是一个基于理解和实际操作经验的概括步骤,旨在指导如何进行这一集成:步骤1:安装Kubernetes集群确保你已经......
  • Multi-View Graph Convolutional Network for Multimedia Recommendation
    目录概符号说明MGCNMotivationBehavior-GuidedPurifierMulti-ViewInformationEncoderBehavior-AwareFuserPredicitonOptimation代码YuP.,TanZ.,LuG.andBaoB.Multi-viewgraphconvolutionalnetworkformultimediarecommendation.MM,2023.概本文主要处理模......
  • 尝试 `npm install @xxxxx` ,或者添加一个包含 `declare module ‘xxxxx‘;` 的新声明(.
    我们在vue3+ts的项目中,有时候安装插件,在导入文件使用的时候会出现上面的问题,这是因为插件库中并没有ts的.d.ts类型的声明文件,所以我们在导入使用的时候一直报警告,无法使用。要解决这个问题,根据他的提示有两种解决方案。方案一:根据提醒安装对应的ts类型插件即可。方案二:......
  • .Net MinimalApis响应返回值
    前言文本主要讲MinimalApis中的使用自定义IResultModel和系统自带IResult做响应返回值。MinimalApis支持以下类型的返回值:string-这包括Task<string>和ValueTask<string>T(任何其他类型)-这包括Task<T>和ValueTask<T>基于IResult-这包括Task<IResult>和......
  • RS485Modbus转Profinet网关多通道轮询配置方法
    RS485Modbus转Profinet网关(XD-MDPN100)有效地转换并实现多通道轮询,可以提高系统的稳定性和效率。在现代工业自动化控制系统中,RS485Modbus转Profinet网关(XD-MDPN100)能够连接不同类型的设备和传感器,并实现数据的快速传输和处理。打开博图加载PLC,在这里使用的是1200PLC,配置PLC的IP......
  • Kubernetes之Pod基本原理与实践
    一、Pod的定义与基本用法1.Pod是什么Pod是可以在Kubernetes中创建和管理的、最小的可部署的计算单元。Pod不是进程,而是容器运行的环境。Pod所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器。当Pod包含多个应用容器时,这些容器的应用之间应该是......
  • Performance Improvements in .NET 8 & 7 & 6 -- File I/O【翻译】
    Net8.0FileI/O.NET6对如何实现文件I/O进行了重大改革,重写了FileStream类,引入了RandomAccess类以及大量的其他更改。.NET8通过进一步改进文件I/O性能而继续提升性能。对于提高系统性能的一种有趣的方法是取消操作。毕竟,最快的工作是不做,而取消操作是关于停止不必要的额外工......