当前位置:首页 > C# > 正文

C#程序集(Assembly)的加载与卸载详解(小白也能掌握的.NET动态程序集管理教程)

在C#和.NET开发中,程序集(Assembly)是部署、版本控制、重用、激活作用域和安全权限的基本单元。理解如何加载卸载程序集对于构建灵活、模块化、可扩展的应用程序至关重要。本文将从零开始,手把手教你如何在C#中动态加载和卸载程序集,即使是编程新手也能轻松上手。

C#程序集(Assembly)的加载与卸载详解(小白也能掌握的.NET动态程序集管理教程) C#程序集加载 C#程序集卸载 .NET Assembly 动态加载程序集 第1张

什么是程序集(Assembly)?

在.NET中,程序集是一个或多个类型(类、接口等)的集合,通常以 .dll.exe 文件形式存在。它包含了元数据(metadata)和中间语言(IL),由CLR(公共语言运行时)执行。

为什么需要动态加载/卸载程序集?

  • 插件系统:允许应用程序在运行时加载新功能模块
  • 热更新:无需重启应用即可更新部分逻辑
  • 资源隔离:防止不同模块之间相互干扰
  • 节省内存:只在需要时加载,使用完后释放

传统方式:无法卸载的程序集

在.NET Framework 和 .NET Core 早期版本中,一旦程序集被加载到默认AppDomain(或默认AssemblyLoadContext),就无法卸载。例如:

// 这种方式加载的程序集无法卸载Assembly assembly = Assembly.LoadFrom(@"C:\Plugins\MyPlugin.dll");Type type = assembly.GetType("MyPlugin.MyClass");object instance = Activator.CreateInstance(type);

这种方式虽然简单,但存在严重问题:即使你不再使用该程序集,它仍会一直驻留在内存中,导致内存泄漏。

现代解决方案:使用AssemblyLoadContext(.NET Core 3.0+ / .NET 5+)

从.NET Core 3.0开始,微软引入了 System.Runtime.Loader.AssemblyLoadContext 类,它允许我们创建可卸载的程序集上下文。这是实现程序集卸载的关键!

步骤1:创建自定义可卸载的上下文

using System;using System.Reflection;using System.Runtime.Loader;public class UnloadableAssemblyLoadContext : AssemblyLoadContext{    public UnloadableAssemblyLoadContext() : base(isCollectible: true)    {    }    protected override Assembly Load(AssemblyName assemblyName)    {        // 可在此处添加自定义加载逻辑        return null; // 使用默认加载机制    }}

注意构造函数中的 isCollectible: true,这是启用卸载功能的关键参数。

步骤2:使用上下文加载程序集

// 创建可卸载上下文var context = new UnloadableAssemblyLoadContext();// 从文件加载程序集Assembly assembly = context.LoadFromAssemblyPath(@"C:\Plugins\MyPlugin.dll");// 获取类型并创建实例Type pluginType = assembly.GetType("MyPlugin.PluginClass");object pluginInstance = Activator.CreateInstance(pluginType);// 调用方法(假设有一个Run方法)pluginType.GetMethod("Run")?.Invoke(pluginInstance, null);

步骤3:卸载程序集

要卸载程序集,必须确保:

  1. 没有对该程序集中任何对象的引用
  2. 调用上下文的 Unload() 方法
  3. 触发垃圾回收(GC)
// 释放所有引用pluginInstance = null;assembly = null;// 卸载上下文context.Unload();// 强制垃圾回收(实际项目中应谨慎使用)GC.Collect();GC.WaitForPendingFinalizers();// 验证是否已卸载bool isUnloaded = context.IsCollectible &&                   !context.Assemblies.Any();Console.WriteLine($"程序集已卸载: {isUnloaded}");

完整示例:插件加载器

using System;using System.IO;using System.Linq;using System.Reflection;using System.Runtime.Loader;class Program{    static void Main()    {        string pluginPath = @"./Plugins/HelloPlugin.dll";                if (!File.Exists(pluginPath))        {            Console.WriteLine("插件文件不存在!");            return;        }        var context = new UnloadableAssemblyLoadContext();        try        {            Assembly assembly = context.LoadFromAssemblyPath(Path.GetFullPath(pluginPath));            Type pluginType = assembly.GetType("HelloPlugin.HelloClass");            dynamic plugin = Activator.CreateInstance(pluginType);                        plugin.SayHello(); // 调用插件方法        }        finally        {            // 清理资源            context.Unload();            GC.Collect();            GC.WaitForPendingFinalizers();            Console.WriteLine("插件已卸载。");        }    }}

注意事项与最佳实践

  • 不要跨上下文传递对象引用:这会导致上下文无法卸载
  • 避免使用static字段:它们会持有对程序集的强引用
  • 使用弱引用(WeakReference)进行调试
  • 仅在必要时强制GC:频繁调用GC会影响性能

总结

通过使用 AssemblyLoadContext 并设置 isCollectible: true,我们现在可以在C#中实现真正的程序集动态加载与卸载。这对于构建插件系统、模块化架构或需要热更新功能的应用非常有用。记住关键点:清理所有引用 → 调用Unload() → 触发GC

掌握这些技巧后,你就能更高效地管理应用程序的内存和模块生命周期。希望这篇关于C#程序集加载C#程序集卸载.NET Assembly动态加载程序集的教程对你有所帮助!