You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 05:50:35