如何避免卸载AppDomain后prelude.dll程序集驻留于默认AppDomain?
问题分析与解决方案
你遇到的问题确实是因为跨域操作时,默认AppDomain被迫加载了prelude.dll的元数据——根源在于你直接在默认域中调用了prelude_domain.Load和CreateInstanceAndUnwrap,这两个操作都会触发默认域加载目标程序集来生成跨域代理的类型信息,导致程序集驻留无法卸载。
要解决这个问题,核心思路是把所有涉及prelude.dll的加载和调用逻辑完全隔离到目标AppDomain内部,不让默认域接触到prelude.dll的类型元数据。具体可以通过以下步骤实现:
1. 创建一个跨域代理类
在默认域已加载的程序集(比如你的appdomaintest项目)中,定义一个继承自MarshalByRefObject的代理类,这个类的职责是在目标AppDomain内部加载prelude.dll并执行所需操作。因为这个类属于默认域已有的程序集,所以跨域调用它时不需要加载额外程序集。
2. 修改主程序逻辑
不再直接在默认域调用目标域的Load或CreateInstanceAndUnwrap,而是通过代理类间接完成所有操作,确保prelude.dll只在目标域中加载。
修改后的代码
主程序代码(appdomaintest)
namespace appdomaintest { // 跨域代理类,负责在目标域中执行prelude的加载和调用 class PreludeProxy : MarshalByRefObject { public void RunPreludeLogic() { // 此方法在目标AppDomain中执行,加载prelude.dll var asm = System.Reflection.Assembly.Load("prelude"); var myint = asm.CreateInstance("prelude.MyInt"); myint.GetType().GetMethod("show").Invoke(myint, new object[0]); } } class Program { static void log_assemblies(AppDomain domain) { foreach (var item in domain.GetAssemblies()) { Console.WriteLine($"{domain.FriendlyName} : {item.FullName}"); } } static void Main(string[] args) { { var prelude_domain = AppDomain.CreateDomain("PreludeDomain#1", null, null); // 创建代理类实例,此操作不会加载prelude.dll到默认域 var proxy = (PreludeProxy)prelude_domain.CreateInstanceAndUnwrap( typeof(PreludeProxy).Assembly.FullName, typeof(PreludeProxy).FullName ); // 通过代理执行目标域内的逻辑 proxy.RunPreludeLogic(); AppDomain.Unload(prelude_domain); Console.WriteLine("AppDomain was unloaded"); } log_assemblies(AppDomain.CurrentDomain); } } }
prelude.dll代码保持不变
public class MyInt : MarshalByRefObject { public MyInt() { Console.WriteLine($"MyInt was constructed in {AppDomain.CurrentDomain.FriendlyName}"); } public void show() { Console.WriteLine($"show in {AppDomain.CurrentDomain.FriendlyName}"); } }
运行结果验证
修改后运行程序,输出会变成:
MyInt was constructed in PreludeDomain#1 show in PreludeDomain#1 AppDomain was unloaded appdomaintest.exe : mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 appdomaintest.exe : appdomaintest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
可以看到默认AppDomain中已经没有prelude.dll的条目了,说明程序集没有被加载到默认域,卸载目标域后就完全释放了。
关键原理说明
PreludeProxy属于默认域已加载的appdomaintest程序集,所以创建跨域代理时,默认域不需要加载任何额外程序集。RunPreludeLogic方法是在目标AppDomain内部执行的,所有prelude.dll的加载和类型实例化都发生在目标域中,默认域完全接触不到prelude的类型元数据,也就不会加载该程序集。- 这种方式完美隔离了目标程序集的加载上下文,后续你修改
prelude.dll后,重新创建AppDomain并通过代理调用,就能加载最新版本的程序集,且不会在默认域留下残留。
内容的提问来源于stack exchange,提问作者Alex Aparin
相关产品推荐
相关产品推荐

