https://www.toutiao.com/article/7470454747691074100/

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.");
    }
}
  1. 常见场景
    3.1 插件系统
    使用 AssemblyLoadContext 可以实现动态加载和卸载插件,而不会影响主应用程序的运行。
    示例:一个应用程序支持多个插件,每个插件都独立加载到自己的 AssemblyLoadContext 中。
    3.2 热更新
    在某些场景下,可能需要在不重启应用程序的情况下更新某些模块。通过 AssemblyLoadContext,可以卸载旧版本的程序集并加载新版本。
    3.3 隔离加载
    如果需要加载不受信任的代码(如第三方库),可以将其加载到独立的 AssemblyLoadContext 中,以避免对主应用程序的影响。
  2. 注意事项
    4.1 程序集卸载的限制
    即使调用了 Unload() 方法,程序集并不会立即被卸载。只有在垃圾回收器运行并且没有任何引用指向该上下文时,程序集才会被真正卸载。
    因此,建议在调用 Unload() 后手动触发垃圾回收:
    GC.Collect(); GC.WaitForPendingFinalizers();
    4.2 避免跨上下文引用
    不要在不同上下文之间共享对象实例。如果一个对象是从某个上下文中加载的,则不应在另一个上下文中使用它。
    4.3 性能开销
    动态加载和卸载程序集会带来一定的性能开销,因此应谨慎使用,尤其是在高频场景中。
  3. 总结
    AssemblyLoadContext 提供了强大的功能,允许开发者动态加载和卸载程序集,从而实现插件系统、热更新和隔离加载等高级功能。然而,在使用时需要注意其限制和潜在的性能问题。
文档更新时间: 2025-02-13 08:54   作者:admin