动态加载程序集调用Assembly.GetTypes()抛ReflectionTypeLoadException及非公共成员访问问题
动态加载程序集异常修复与非公共成员反射指南
一、先搞定ReflectionTypeLoadException异常
异常根源
你碰到的ReflectionTypeLoadException,核心原因是BasicAuthenticationHandler继承了AuthenticationHandler抽象类,但没有正确实现抽象方法HandleAuthenticateAsync。哪怕你代码里写了实现,也可能是以下情况导致的:
- 方法签名不匹配:抽象方法是
protected abstract Task<AuthenticateResult> HandleAuthenticateAsync(),实现必须是protected override且参数、返回值完全一致 - 编译优化搞砸了:Release编译的优化可能导致方法元数据丢失,或者Debug编译的调试信息不全
- 框架版本兼容问题:加载程序是.NET7,目标程序集是.NET6,若引用的
Microsoft.AspNetCore.Authentication包版本不一致,也可能引发签名不匹配
修复方案(你有权限改项目设置,直接按下面来)
- 检查方法实现:打开
BasicAuthenticationHandler的代码,确保实现代码如下:
protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // 这里填你的业务逻辑,比如验证账号密码 return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(/* 你的票据信息 */))); }
- 调整编译配置:如果是Release编译出的问题,临时改成Debug编译,或者在项目文件里禁用优化、开启完整调试信息:
<PropertyGroup> <Optimize>false</Optimize> <DebugType>full</DebugType> </PropertyGroup>
- 对齐依赖版本:确保加载程序(.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和加载逻辑有几个坑,会导致依赖加载失败或重复加载:
- 每次加载都创建新的
AssemblyLoader实例,同一程序集被加载多次,会引发类型不匹配 - 依赖加载逻辑混用
DependencyContext和本地文件,容易找不到依赖 - 静态字典没做同步,多线程下会出问题
修正后的完整代码
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
相关产品推荐
相关产品推荐

