.NET 9 中的新增功能:反射增强功能

作者:微信公众号:【架构师老卢】
7-19 10:26
26

概述:虽然反射对于在运行时检查类型和与类型交互一直非常有用,但 .NET 9 更进一步,不仅允许我们创建动态程序集,还可以保存它们以供以后使用。以前,构建程序集和跟踪动态创建的类型的反射数据仅限于单个用例。这意味着我们可以即时创建程序集,但无法存储它以备将来使用。这对于我们来说是一个障碍,因为我们习惯于在框架中保存程序集的灵活性。产品/服务的新功能:在运行时使用自定义类型、方法甚至枚举构造程序集。将这些动态程序集保存到磁盘上,从而为它们提供有形的形式。这使我们能够在未来的应用程序实例中或跨不同的项目利用保存的程序集。此外,这还提供了共享包含自定义逻辑的预生成程序集的灵活性。优势和用例这项新功能具有以

虽然反射对于在运行时检查类型和与类型交互一直非常有用,但 .NET 9 更进一步,不仅允许我们创建动态程序集,还可以保存它们以供以后使用。

以前,构建程序集和跟踪动态创建的类型的反射数据仅限于单个用例。这意味着我们可以即时创建程序集,但无法存储它以备将来使用。这对于我们来说是一个障碍,因为我们习惯于在框架中保存程序集的灵活性。

产品/服务的新功能:

  • 在运行时使用自定义类型、方法甚至枚举构造程序集。
  • 将这些动态程序集保存到磁盘上,从而为它们提供有形的形式。这使我们能够在未来的应用程序实例中或跨不同的项目利用保存的程序集。此外,这还提供了共享包含自定义逻辑的预生成程序集的灵活性。

优势和用例

这项新功能具有以下几个优点:

  • 想象一下,生成特定于用户输入的代码,或根据配置动态加载模块。适应性和可扩展性应用程序的可能性显著增加。
  • 通过预构建和保存包含可重用逻辑的程序集,我们可以专注于核心功能,从而加速开发。

过程

创建和保存持久化程序集的过程与使用非持久化程序集的过程类似。您仍然可以定义类型、方法和编写指令(称为中间语言或 IL)来定义其行为。神奇之处在于新方法,它照顾到节省方面。

虽然持久化程序集提供了令人兴奋的可能性,但重要的是要考虑以下几点:

  • 性能: 与传统的静态编译代码相比,反射和动态代码生成可能会产生轻微的性能损失。在性能关键型方案中使用它们之前,请评估权衡取舍。
  • 类型安全: 使用反射访问属性和方法时要小心。如果不确保类型兼容性,不正确的使用可能会导致运行时错误。

代码示例和说明

让我们使用一个简单的代码示例来巩固我们的理解。

使用非持久化程序集的代码(以前的 .NET 版本):

using System.Reflection.Emit;
using System.Reflection;

public class DynamicAssemblyExample
{
    public static void Main(string[] args)
    {
        // Create a non-persisted assembly
        AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(
            new AssemblyName("MyAssembly"),
            AssemblyBuilderAccess.Run); // Access needed for immediate execution

        UseAssembly(ab); // Use the assembly directly (no need to load from disk)
    }

    public static void UseAssembly(AssemblyBuilder ab)
    {
        ModuleBuilder module = ab.GetDynamicModule("MyModule");

        TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);

        MethodBuilder mb = tb.DefineMethod(
            "SumMethod",
            MethodAttributes.Public | MethodAttributes.Static,
            typeof(int), new[] { typeof(int), typeof(int) });
        ILGenerator il = mb.GetILGenerator();

        // Generate IL instructions for multiplication (instead of addition)
        il.Emit(OpCodes.Ldarg_0);  // Load first argument onto stack
        il.Emit(OpCodes.Ldarg_1);  // Load second argument onto stack
        il.Emit(OpCodes.Mul);       // Multiply the two arguments (changed from Add)
        il.Emit(OpCodes.Ret);       // Return the result

        tb.CreateType();

        // Get the method info directly from the in-memory assembly
        Type type = ab.GetType("MyType");
        MethodInfo method = type.GetMethod("SumMethod");
        Console.WriteLine(method.Invoke(10, new object[] { 5, 10 })); // Call SumMethod with arguments
    }
}

使用持久程序集编写代码 (.NET 9)

using System.Reflection.Emit;
using System.Reflection;

public class DynamicAssemblyExample
{
    public static void Main(string[] args)
    {
        // Change the path as needed
        string assemblyPath = "MyDynamicAssembly.dll";

        // Create and save the assembly
        CreateAndSaveAssembly(assemblyPath);

        // Use the saved assembly
        UseAssembly(assemblyPath);
    }

    public static void CreateAndSaveAssembly(string assemblyPath)
    {
        AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(
            new AssemblyName("MyAssembly"),
            typeof(object).Assembly);

        TypeBuilder tb = ab.DefineDynamicModule("MyModule")
            .DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);

        MethodBuilder mb = tb.DefineMethod(
            "SumMethod",
            MethodAttributes.Public | MethodAttributes.Static,
            typeof(int), new[] { typeof(int), typeof(int)});
        ILGenerator il = mb.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Mul);
        il.Emit(OpCodes.Ret);

        tb.CreateType();
        ab.Save(assemblyPath);
    }

    public static void UseAssembly(string assemblyPath)
    {
        Assembly assembly = Assembly.LoadFrom(assemblyPath);
        Type type = assembly.GetType("MyType");
        MethodInfo method = type.GetMethod("SumMethod");
        Console.WriteLine(method.Invoke(10, new object[] { 5, 10 }));
    }

}

创建持久化程序集

  • DefinePersistedAssembly:此方法创建一个具有持久性功能的新实例。:我们为程序集提供一个名称(“MyAssembly”)。:我们引用核心组件 () 作为基础。

定义类型和方法

  • 我们在名为“MyModule”的模块中定义了一个名为“MyType”的类型。在“MyType”中,我们定义了一个名为“SumMethod”的静态方法,该方法接受两个整数并返回它们的总和。

生成 IL 指令:

  • 为“SumMethod”创建说明:将两个参数加载到堆栈上。添加参数。返回结果。

完成和保存:

  • CreateType:完成“MyType”类的定义。 :将整个动态程序集保存到指定路径 ()。

加载和使用程序集:

  • Assembly.LoadFrom:从磁盘加载保存的程序集。我们从加载的程序集中检索“MyType”类和“SumMethod”。最后,我们用两个整数(5 和 10)调用“SumMethod”,结果 (15) 被打印到控制台。

简单地说,此代码演示了使用简单的“SumMethod”创建和保存动态程序集所涉及的核心概念。使用 .NET 9 中的持久化程序集。既然您已经对持久化程序集有了深入的理解,为什么不在自己的项目中试验它们呢?

相关留言评论
昵称:
邮箱:
阅读排行