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

动态加载程序集调用Assembly.GetTypes()抛ReflectionTypeLoadException及非公共成员访问问题

动态加载程序集异常修复与非公共成员反射指南

一、先搞定ReflectionTypeLoadException异常

异常根源

你碰到的ReflectionTypeLoadException,核心原因是BasicAuthenticationHandler继承了AuthenticationHandler抽象类,但没有正确实现抽象方法HandleAuthenticateAsync。哪怕你代码里写了实现,也可能是以下情况导致的:

  • 方法签名不匹配:抽象方法是protected abstract Task<AuthenticateResult> HandleAuthenticateAsync(),实现必须是protected override且参数、返回值完全一致
  • 编译优化搞砸了:Release编译的优化可能导致方法元数据丢失,或者Debug编译的调试信息不全
  • 框架版本兼容问题:加载程序是.NET7,目标程序集是.NET6,若引用的Microsoft.AspNetCore.Authentication包版本不一致,也可能引发签名不匹配

修复方案(你有权限改项目设置,直接按下面来)

  1. 检查方法实现:打开BasicAuthenticationHandler的代码,确保实现代码如下:
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
    // 这里填你的业务逻辑,比如验证账号密码
    return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(/* 你的票据信息 */)));
}
  1. 调整编译配置:如果是Release编译出的问题,临时改成Debug编译,或者在项目文件里禁用优化、开启完整调试信息:
<PropertyGroup>
  <Optimize>false</Optimize>
  <DebugType>full</DebugType>
</PropertyGroup>
  1. 对齐依赖版本:确保加载程序(.NET7)和目标程序集(.NET6)引用的Microsoft.AspNetCore.Authentication包版本一致,或者.NET7兼容该版本的依赖。

二、反射访问非公共类型/成员的方法

要查看非公共的类型、字段、方法,关键是在反射调用时指定BindingFlags绑定标志,常用组合如下:

  • 非公共类型:BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static
  • 非公共实例成员:BindingFlags.NonPublic | BindingFlags.Instance
  • 非公共静态成员:BindingFlags.NonPublic | BindingFlags.Static

实操示例

// 获取程序集里所有非公共类型
Type[] nonPublicTypes = loadedAssembly.GetTypes()
    .Where(t => !t.IsPublic)
    .ToArray();

// 以BasicAuthenticationHandler为例,获取它的非公共方法
Type handlerType = loadedAssembly.GetType("My.Namespace.Api.Attributes.BasicAuthenticationHandler");
MethodInfo secretMethod = handlerType.GetMethod("MyNonPublicMethod", BindingFlags.NonPublic | BindingFlags.Instance);

// 获取非公共字段
FieldInfo secretField = handlerType.GetField("_myPrivateField", BindingFlags.NonPublic | BindingFlags.Instance);

三、修正你的动态加载代码

你的AssemblyLoader和加载逻辑有几个坑,会导致依赖加载失败或重复加载:

  1. 每次加载都创建新的AssemblyLoader实例,同一程序集被加载多次,会引发类型不匹配
  2. 依赖加载逻辑混用DependencyContext和本地文件,容易找不到依赖
  3. 静态字典没做同步,多线程下会出问题

修正后的完整代码

internal class Program
{
    public static void Main(string[] args)
    {
        string targetFolder = @"C:\mypublish folder\bin\Debug\net6.0\win-x64";
        // 只创建一个AssemblyLoader实例,避免重复加载
        using var loader = new AssemblyLoader(targetFolder);
        Assembly targetAssembly = loader.LoadFromAssemblyName(new AssemblyName("My.Namespace.Api"));

        // 加载所有依赖
        LoadAllDependencies(loader, targetAssembly);

        // 获取程序集所有类型(包括非公共)
        Type[] allTypes = targetAssembly.GetTypes();
        
        // 示例:遍历非公共类型及其成员
        foreach (Type type in allTypes)
        {
            if (!type.IsPublic)
            {
                Console.WriteLine($"非公共类型:{type.FullName}");
                // 获取所有非公共实例方法
                var nonPublicMethods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (var method in nonPublicMethods)
                {
                    Console.WriteLine($"  非公共方法:{method.Name}");
                }
            }
        }
    }

    // 用HashSet跟踪已加载的程序集,避免重复加载和循环依赖
    private static readonly HashSet<string> _loadedAssemblies = new HashSet<string>(
        AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().FullName));

    private static void LoadAllDependencies(AssemblyLoadContext loader, Assembly assembly)
    {
        string assemblyFullName = assembly.GetName().FullName;
        if (_loadedAssemblies.Contains(assemblyFullName))
            return;

        _loadedAssemblies.Add(assemblyFullName);

        foreach (AssemblyName refName in assembly.GetReferencedAssemblies())
        {
            if (!_loadedAssemblies.Contains(refName.FullName))
            {
                try
                {
                    Assembly refAssembly = loader.LoadFromAssemblyName(refName);
                    LoadAllDependencies(loader, refAssembly);
                }
                catch (FileNotFoundException)
                {
                    // 找不到依赖时的处理,可根据需求调整
                    Console.WriteLine($"警告:未找到依赖程序集 {refName.FullName}");
                }
            }
        }
    }
}

public class AssemblyLoader : AssemblyLoadContext
{
    private readonly string _folderPath;

    // isCollectible设为true,支持后续卸载程序集(可选)
    public AssemblyLoader(string folderPath) : base(isCollectible: true)
    {
        _folderPath = folderPath;
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        // 优先从目标文件夹加载依赖
        string assemblyPath = Path.Combine(_folderPath, $"{assemblyName.Name}.dll");
        if (File.Exists(assemblyPath))
        {
            return LoadFromAssemblyPath(assemblyPath);
        }

        // 本地没有的话,从默认上下文加载系统程序集
        return Default.LoadFromAssemblyName(assemblyName);
    }
}

代码修正说明

  • 单实例AssemblyLoader:避免同一程序集被多次加载,引发类型冲突
  • HashSet跟踪已加载程序集:防止循环加载和重复加载
  • 简化依赖加载逻辑:先本地文件夹,再默认上下文,更稳定
  • 支持程序集卸载:标记isCollectible: true,不需要时可释放资源

四、额外注意点

  • 如果目标程序集是单文件发布的,不能直接加载单个dll,要么禁用单文件发布,要么用工具提取单文件中的程序集
  • 若目标程序集启用了Trim优化,反射访问非公共成员会被限制,需在项目文件中禁用:
<PropertyGroup>
  <PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>

内容的提问来源于stack exchange,提问作者Sh'dynasty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 22:39:51