.Net Core 动态加载与卸载程序集

.Net Core 动态加载与卸载程序集

编码文章call10242025-03-11 12:50:3842A+A-

AssemblyLoadContext 是 .NET Core 和 .NET 5+ 中引入的一个重要功能,用于动态加载和卸载程序集(Assemblies)。它提供了一种灵活的方式来管理程序集的生命周期,尤其是在需要动态加载插件或模块化应用程序时非常有用。

以下是 AssemblyLoadContext 的使用方式、核心概念以及常见场景的详细说明。


1. 核心概念

  • 默认上下文(Default Context)
    • 所有程序集在没有显式指定 AssemblyLoadContext 的情况下,默认会被加载到默认上下文中。
    • 默认上下文中的程序集无法卸载。
  • 自定义上下文(Custom Context)
    • 开发者可以创建自定义的 AssemblyLoadContext,用于隔离加载的程序集。
    • 自定义上下文中的程序集可以通过释放上下文来卸载。
  • 程序集卸载
    • 在 .NET Framework 中,程序集一旦加载就无法卸载。而在 .NET Core 和 .NET 5+ 中,通过 AssemblyLoadContext 可以实现程序集的卸载。

2. 使用方式

2.1 创建自定义AssemblyLoadContext

以下是一个简单的示例,展示如何创建一个自定义的 AssemblyLoadContext 并加载程序集:

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;

public class CustomAssemblyLoadContext : AssemblyLoadContext
{
    public CustomAssemblyLoadContext() : base(isCollectible: true)
    {
        // isCollectible 设置为 true 表示该上下文是可收集的(即支持卸载)
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        // 如果需要自定义加载逻辑,可以在这里实现
        return null; // 返回 null 表示使用默认加载逻辑
    }
}

class Program
{
    static void Main(string[] args)
    {
        // 创建自定义上下文
        var customLoadContext = new CustomAssemblyLoadContext();

        // 加载程序集
        string assemblyPath = Path.Combine(AppContext.BaseDirectory, "MyPlugin.dll");
        Assembly assembly = customLoadContext.LoadFromAssemblyPath(assemblyPath);

        // 获取类型并调用方法
        Type type = assembly.GetType("MyPlugin.MyClass");
        object instance = Activator.CreateInstance(type);
        MethodInfo method = type.GetMethod("SayHello");
        method.Invoke(instance, null);

        // 卸载上下文
        customLoadContext.Unload();
        Console.WriteLine("Assembly unloaded.");
    }
}

2.2 动态加载和卸载程序集

以下是一个更完整的示例,展示如何动态加载和卸载程序集:

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading;

public class PluginLoader : IDisposable
{
    private AssemblyLoadContext _context;

    public PluginLoader()
    {
        _context = new AssemblyLoadContext("PluginContext", isCollectible: true);
    }

    public void LoadAndRunPlugin(string assemblyPath)
    {
        // 加载程序集
        Assembly assembly = _context.LoadFromAssemblyPath(assemblyPath);

        // 获取类型并调用方法
        Type type = assembly.GetType("MyPlugin.MyClass");
        if (type != null)
        {
            object instance = Activator.CreateInstance(type);
            MethodInfo method = type.GetMethod("SayHello");
            method?.Invoke(instance, null);
        }
    }

    public void Unload()
    {
        _context.Unload();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    public void Dispose()
    {
        Unload();
    }
}

class Program
{
    static void Main(string[] args)
    {
        string pluginPath = Path.Combine(AppContext.BaseDirectory, "MyPlugin.dll");

        using (var loader = new PluginLoader())
        {
            loader.LoadAndRunPlugin(pluginPath);
        }

        Console.WriteLine("Plugin unloaded.");
    }
}

3. 常见场景

3.1 插件系统

  • 使用 AssemblyLoadContext 可以实现动态加载和卸载插件,而不会影响主应用程序的运行。
  • 示例:一个应用程序支持多个插件,每个插件都独立加载到自己的 AssemblyLoadContext 中。

3.2 热更新

  • 在某些场景下,可能需要在不重启应用程序的情况下更新某些模块。通过 AssemblyLoadContext,可以卸载旧版本的程序集并加载新版本。

3.3 隔离加载

  • 如果需要加载不受信任的代码(如第三方库),可以将其加载到独立的 AssemblyLoadContext 中,以避免对主应用程序的影响。

4. 注意事项

4.1 程序集卸载的限制

  • 即使调用了 Unload() 方法,程序集并不会立即被卸载。只有在垃圾回收器运行并且没有任何引用指向该上下文时,程序集才会被真正卸载。
  • 因此,建议在调用 Unload() 后手动触发垃圾回收:
  • GC.Collect(); GC.WaitForPendingFinalizers();

4.2 避免跨上下文引用

  • 不要在不同上下文之间共享对象实例。如果一个对象是从某个上下文中加载的,则不应在另一个上下文中使用它。

4.3 性能开销

  • 动态加载和卸载程序集会带来一定的性能开销,因此应谨慎使用,尤其是在高频场景中。

5. 总结

AssemblyLoadContext 提供了强大的功能,允许开发者动态加载和卸载程序集,从而实现插件系统、热更新和隔离加载等高级功能。然而,在使用时需要注意其限制和潜在的性能问题。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4