当前位置:首页 > C# > 正文

C#动态程序集的依赖解析(深入理解AssemblyLoadContext实现运行时加载)

在现代 C# 开发中,C#动态程序集 的使用越来越普遍,特别是在插件系统、模块化架构或热更新场景中。然而,当你在运行时动态加载程序集(Assembly)时,经常会遇到依赖解析的问题:被加载的程序集依赖于其他 DLL,但 .NET 运行时却找不到这些依赖项。

C#动态程序集的依赖解析(深入理解AssemblyLoadContext实现运行时加载) C#动态程序集 依赖解析 AssemblyLoadContext 运行时加载程序集 第1张

为什么需要手动处理依赖解析?

默认情况下,.NET Core/.NET 5+ 使用 AssemblyLoadContext.Default 来加载程序集。当你使用 Assembly.LoadFromAssembly.LoadFile 加载一个外部 DLL 时,如果该 DLL 引用了其他未在默认上下文中加载的程序集,运行时会抛出 FileNotFoundExceptionReflectionTypeLoadException

为了解决这个问题,我们需要自定义一个 AssemblyLoadContext 并重写其 Load 方法,以提供运行时加载程序集的能力。

步骤一:创建自定义 AssemblyLoadContext

首先,我们继承 AssemblyLoadContext 类,并重写 Load 方法:

using System;using System.IO;using System.Reflection;using System.Runtime.Loader;class PluginLoadContext : AssemblyLoadContext{    private readonly string _pluginPath;    public PluginLoadContext(string pluginPath) : base(isCollectible: true)    {        _pluginPath = pluginPath;    }    protected override Assembly Load(AssemblyName assemblyName)    {        // 尝试从插件目录加载依赖        string assemblyPath = Path.Combine(_pluginPath, assemblyName.Name + ".dll");        if (File.Exists(assemblyPath))        {            return LoadFromAssemblyPath(assemblyPath);        }        // 如果找不到,则交给默认上下文处理(例如 .NET 标准库)        return null;    }}

在这个类中,我们指定了插件所在的目录 _pluginPath。当运行时需要加载某个依赖程序集时,它会先尝试在该目录下查找对应的 DLL 文件。如果找不到,就返回 null,让 .NET 默认机制继续处理(比如加载系统库)。

步骤二:使用自定义上下文加载程序集

接下来,我们使用这个上下文来加载目标插件程序集:

// 插件所在目录string pluginDirectory = @"C:\MyPlugins";// 创建自定义加载上下文var context = new PluginLoadContext(pluginDirectory);// 加载主插件程序集string pluginDllPath = Path.Combine(pluginDirectory, "MyPlugin.dll");Assembly pluginAssembly = context.LoadFromAssemblyPath(pluginDllPath);// 获取类型并创建实例Type pluginType = pluginAssembly.GetType("MyPlugin.PluginClass");object pluginInstance = Activator.CreateInstance(pluginType);// 调用方法(假设有一个 Run 方法)pluginType.GetMethod("Run")?.Invoke(pluginInstance, null);// 可选:卸载上下文(因为 isCollectible: true)context.Unload();

通过这种方式,所有在 MyPlugin.dll 中引用的、位于同一目录下的依赖 DLL 都会被自动加载,解决了依赖解析的问题。

注意事项与最佳实践

  • 确保所有依赖项都放在同一个插件目录中,或在 Load 方法中扩展查找逻辑。
  • 使用 isCollectible: true 可以在不再需要插件时卸载整个上下文,避免内存泄漏。
  • 不要混用 Assembly.LoadFrom 和自定义 AssemblyLoadContext,否则可能导致类型不兼容问题。
  • 对于 .NET Framework 用户,此方法不适用(它使用 AppDomain),本教程仅适用于 .NET Core 3.0 及以上版本。

总结

掌握 C#动态程序集依赖解析机制,是构建灵活、可扩展应用程序的关键。通过自定义 AssemblyLoadContext,我们可以完全控制运行时加载程序集的行为,确保插件及其依赖能够正确加载和执行。无论你是开发插件系统、微服务模块,还是需要热更新功能,这套方法都能为你提供坚实的基础。

希望这篇教程能帮助你轻松应对动态加载中的依赖问题!如果你有任何疑问,欢迎在评论区交流。