如何解决AssemblyLoadContext加载对象后无法转换为接口的问题
问题描述
使用HostAssemblyLoadContext加载程序集后,获取到Optimizer实例无法转换为IOptimizer接口,断言失败、类型检查始终不成立;但不使用AssemblyLoadContext时逻辑完全正常。核心原因是IOptimizer被加载为两个不同的类型(二者来自同一DLL),分别属于默认上下文和自定义的HostAssemblyLoadContext。
相关代码
异常触发代码
/// <summary> /// 此处asm通过HostAssemblyLoadContext加载 /// </summary> /// <param name="asm"></param> /// <param name="alc"></param> private void OptimizerAction(Assembly asm, HostAssemblyLoadContext alc) { var types = asm.GetTypes().Where(type => type.GetInterface(nameof(IOptimizer)) != null).ToList(); if (types.Count != 1) { return; } Type optimizerType = types[0]; MethodInfo? getInstance = optimizerType.GetMethod("GetInstance", BindingFlags.Static | BindingFlags.Public); if (getInstance != null) { object? obj = getInstance.Invoke(null, null); if (obj != null) { Debug.Assert(obj.GetType().Name == "Optimizer"); var iopt = obj as IOptimizer; Debug.Assert(iopt != null); // 断言失败 ******* if (obj is IOptimizer opt) // 永远为假 ******* { opt.Optimize(); // 此处永远不会执行:通过AssemblyLoadContext加载时无法识别IOptimizer } } } }
参考实现代码
public class Optimizer : IOptimizer { public static IOptimizer GetInstance() { return new Optimizer(); } // 其他实现代码... }
解决方案
要实现和不使用AssemblyLoadContext时相同的行为,核心是确保定义IOptimizer的程序集在所有上下文里只加载一次,或者统一使用同一上下文的类型。
1. 共享接口程序集(推荐方案)
修改自定义HostAssemblyLoadContext的加载逻辑,让共享的接口程序集从默认上下文加载,避免重复加载:
public class HostAssemblyLoadContext : AssemblyLoadContext { private readonly AssemblyDependencyResolver _resolver; public HostAssemblyLoadContext(string pluginPath) : base(isCollectible: true) { _resolver = new AssemblyDependencyResolver(pluginPath); } protected override Assembly? Load(AssemblyName assemblyName) { // 替换为你的共享接口程序集名称 if (assemblyName.Name.Equals("YourSharedInterfaceAssembly", StringComparison.OrdinalIgnoreCase)) { // 从默认上下文加载,确保类型唯一 return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName); } string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath != null) { return LoadFromAssemblyPath(assemblyPath); } return null; } }
这样,自定义上下文会复用默认上下文里的IOptimizer类型,类型转换和检查就能正常工作。
2. 反射调用接口方法(临时 workaround)
如果暂时无法调整加载策略,可以通过反射直接调用目标方法,跳过强类型转换:
private void OptimizerAction(Assembly asm, HostAssemblyLoadContext alc) { var types = asm.GetTypes().Where(type => type.GetInterface(nameof(IOptimizer)) != null).ToList(); if (types.Count != 1) { return; } Type optimizerType = types[0]; MethodInfo? getInstance = optimizerType.GetMethod("GetInstance", BindingFlags.Static | BindingFlags.Public); if (getInstance != null) { object? obj = getInstance.Invoke(null, null); if (obj != null) { // 反射获取并调用Optimize方法 MethodInfo? optimizeMethod = obj.GetType().GetMethod("Optimize", BindingFlags.Instance | BindingFlags.Public); optimizeMethod?.Invoke(obj, null); } } }
3. 配置AssemblyLoadContext的共享依赖
如果使用.NET Core 3.0+,可以通过AssemblyLoadContext的构造参数或依赖配置,明确标记共享程序集,避免重复加载。比如在插件的.deps.json里将接口程序集标记为runtime依赖,让自定义上下文优先复用默认上下文的加载结果。
内容的提问来源于stack exchange,提问作者Eric Ouellet
相关产品推荐
相关产品推荐

