assemblyloadcontext通过创建独立的程序集加载环境解决了dll hell和动态卸载难题,它允许每个插件在隔离的上下文中加载所需版本的依赖,避免冲突,并支持在运行时卸载整个上下文以释放资源;其核心机制是通过自定义assemblyloadcontext子类并重写load方法实现“子级优先”的解析策略,确保插件优先使用自身依赖,同时可通过assemblydependencyresolver定位依赖路径;为实现安全卸载,必须消除所有对上下文内对象的强引用,包括取消事件订阅、清理静态变量、停止线程与任务,并可通过实现iplugincleanup接口或监听unloading事件来执行清理操作,最终在无引用残留的情况下调用unload()方法完成卸载,从而实现高效、可扩展且资源可控的插件化架构。

`.NET的AssemblyLoadContext类提供了一种强大的机制,用于在单个应用程序域内隔离程序集的加载。简单来说,它就像是为你的程序集创建了一个个独立的“小房间”,每个房间都有自己的规则和环境,从而有效避免了不同组件之间因依赖版本冲突而引发的“DLL Hell”问题,并允许动态地卸载不再需要的程序集,释放资源。
在.NET Core/.NET 5+的世界里,
AssemblyLoadContext
AssemblyLoadContext.Default
想象一下,你正在构建一个插件系统。每个插件可能依赖不同版本的同一个库,比如插件A需要
Newtonsoft.Json
AssemblyLoadContext
当你调用
new CustomAssemblyLoadContext().LoadFromAssemblyPath("path/to/plugin.dll")Default
Load
最令人兴奋的是它的卸载能力。当一个插件不再需要时,你可以通过释放对该
AssemblyLoadContext
Unload()
说实话,刚接触这个类的时候,我个人觉得有点复杂,不就是加载个DLL嘛,以前不是也能加载吗?但深入了解后才发现,它解决了太多以前让人头疼的问题。最核心的,无疑是“DLL Hell”这个老生常谈的话题。设想一下,你的主程序依赖了某个库的A版本,然后你又想集成一个第三方组件,结果这个组件依赖了同一个库的B版本。在没有
AssemblyLoadContext
AssemblyLoadContext
除此之外,它还带来了动态卸载的能力。以前,一旦程序集加载到内存,除非整个进程退出,否则它会一直占用资源。这对于需要热更新、频繁加载/卸载组件的场景(比如一些CAD软件的插件、游戏MOD加载器,或者云服务中动态部署的小型应用)来说,是极大的限制。
AssemblyLoadContext
自定义
AssemblyLoadContext
Load
AssemblyLoadContext
最常见的自定义场景就是改变默认的“父级优先”委托模型。默认情况下,当一个
AssemblyLoadContext
Default
然而,在插件架构中,你可能需要“子级优先”策略。这意味着你的自定义上下文会优先尝试在自己的加载路径中找到并加载程序集,只有当它自己找不到时,才会委托给父上下文。这对于确保插件使用其自带的特定版本依赖非常有用,即使父上下文有不同版本,插件也能保持其独立性。实现这种策略,你需要在重写的
Load
LoadFromAssemblyPath
LoadFrom*
Load
base.Load(assemblyName)
public class PluginLoadContext : AssemblyLoadContext
{
private readonly string _pluginPath;
private readonly AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath) : base(isCollectible: true)
{
_pluginPath = pluginPath;
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
// 尝试在插件的依赖路径中解析
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
// 如果插件路径中没有,再尝试委托给默认上下文(父级)
// 这样可以共享框架程序集,避免重复加载
return null; // 返回null表示让基类或默认上下文处理
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
// 同样,处理非托管DLL的加载
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero; // 返回IntPtr.Zero表示让基类或默认上下文处理
}
}
// 使用示例
// string pluginAssemblyPath = "path/to/your/Plugin.dll";
// var context = new PluginLoadContext(pluginAssemblyPath);
// Assembly pluginAssembly = context.LoadFromAssemblyPath(pluginAssemblyPath);
// // ... 使用插件中的类型
// context.Unload(); // 当不再需要时卸载除了这种显式的父子委托,你还可以实现更复杂的逻辑,比如从网络位置加载、从数据库加载二进制数据,或者根据某些配置规则动态选择加载路径。关键在于,
Load
使用
AssemblyLoadContext
最大的挑战之一是类型共享问题。即使两个
AssemblyLoadContext
Newtonsoft.Json
JObject
JObject
InvalidCastException
另一个让人头疼的问题是确保完全卸载。当你调用
Unload()
为了确保安全卸载,你需要采取以下策略:
Unload()
WeakReference<T>
IDisposable
IPluginCleanup
Unloading
AssemblyLoadContext
Unloading
FreeLibrary
AssemblyLoadContext
LoadUnmanagedDll
卸载失败不会立即抛出异常,但
Unload()
InvalidOperationException
以上就是.NET的AssemblyLoadContext类如何隔离程序集加载?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号