c# 如何动态加载程序集

来源:这里教程网 时间:2026-02-21 17:36:31 作者:

AssemblyLoadContext.Load 适用于 .NET Core / .NET 5+ 的隔离加载

在 .NET Core 及更高版本中,

Assembly.LoadFrom
会把程序集加载进默认上下文,无法卸载,容易造成内存泄漏。真正可卸载的动态加载必须用
AssemblyLoadContext

关键点:

继承
AssemblyLoadContext
并重写
Load
方法,控制依赖解析逻辑
新建实例时传
isCollectible: true
,否则仍不可卸载
加载后必须显式调用
context.Unload()
,且需确保无任何托管引用残留(包括事件订阅、静态字段、委托缓存)
var context = new AssemblyLoadContext(isCollectible: true);
try
{
    var asm = context.LoadFromAssemblyPath(@"C:\plugins\MyPlugin.dll");
    // 使用 asm 创建类型实例等...
}
finally
{
    context.Unload(); // 必须调用,但可能阻塞直到 GC 回收完成
}

Assembly.LoadFrom 在 .NET Framework 中最常用但不可卸载

如果你还在用 .NET Framework(如 4.8),

Assembly.LoadFrom
是最直接的方式,但它会把程序集加载进
Default
上下文,整个进程生命周期内无法释放。

常见误用:

反复调用
LoadFrom
同一路径 → 抛出
FileLoadException
:“不能为同一个程序集加载多个版本”
试图用
AppDomain.Unload
卸载 → .NET Framework 中仅对非默认 AppDomain 有效,且已标记为过时
try
{
    var asm = Assembly.LoadFrom(@"C:\legacy\Tool.dll");
    var type = asm.GetType("Tool.Processor");
    var inst = Activator.CreateInstance(type);
}
catch (FileLoadException ex) when (ex.Message.Contains("same assembly"))
{
    // 已加载,从 AppDomain.CurrentDomain.GetAssemblies() 查找复用
}

使用 AssemblyDependencyResolver 避免 MissingMethodException

动态加载的程序集若依赖其他 DLL(比如 Newtonsoft.Json 或自定义基础库),不处理依赖会导致运行时报

MissingMethodException
FileNotFoundException

AssemblyDependencyResolver
能自动读取
.deps.json
文件(发布时生成),定位依赖路径:

只在 .NET Core 3.0+ 可用,需确保目标程序集是通过
dotnet publish
输出的
构造时传入主程序集路径(不是被加载 DLL 的路径),resolver 才能正确解析依赖树 配合自定义
AssemblyLoadContext
Load
方法使用,否则依赖仍会 fallback 到默认上下文
var resolver = new AssemblyDependencyResolver(assemblyPath); // 注意:传的是 MyPlugin.deps.json 同级的主程序集路径
var context = new CustomLoadContext(resolver); // 自定义 Load 方法中调用 resolver.ResolveAssemblyToPath(...)

反射调用前务必检查 Assembly.IsDynamic 和目标类型可见性

动态加载后,即使

Assembly
对象存在,也可能因以下原因导致
GetType
返回 null 或
Activator.CreateInstance
失败:

asm.IsDynamic
为 true → 说明是
AssemblyBuilder
生成的动态程序集,不能用
LoadFrom
加载方式获取
目标类型是
internal
且未用
[InternalsVisibleTo]
开放给调用方程序集
类型位于嵌套命名空间或泛型定义中,
GetType("Name")
写法不完整(应为
"NS.Outer+Inner`1[[T]]"

建议先遍历

asm.GetTypes()
确认类型是否存在,再用
BindingFlags.NonPublic | BindingFlags.Public
获取构造器。

加载本身不难,难的是依赖路径、上下文生命周期和类型可见性的组合判断——这三个地方错一个,错误现象就可能完全不同。

相关推荐

热文推荐