AppDomain 在现代 C# 中基本已弃用
从 .NET Core 1.0 开始,
AppDomain类型就不再被支持;.NET 5+ 完全移除了它。如果你正在写新项目(尤其是面向 .NET 5/6/7/8),
AppDomain不是可用选项——不是“怎么用”的问题,而是“不能用”。仅在维护遗留的 .NET Framework(4.x)桌面应用时才可能遇到它。
为什么 AppDomain 曾被用于隔离程序集
在 .NET Framework 时代,
AppDomain是进程内的逻辑隔离边界,主要用来: 动态加载/卸载程序集(
Assembly.LoadFrom加载后无法卸载,但放在独立
AppDomain中可整体卸载) 限制权限(配合
AppDomain.CreateDomain的
PermissionSet参数) 捕获未处理异常(
AppDomain.CurrentDomain.UnhandledException)
但它的隔离性并不彻底(共享类型对象、静态字段仍可能跨域泄漏),且性能开销大、调试困难,早已被更轻量的方案替代。
替代 AppDomain 的现代做法
对应旧有需求,现在应转向:
动态加载与卸载程序集:使用AssemblyLoadContext(.NET Core+),支持
Unload();需手动管理上下文生命周期,避免内存泄漏 沙箱/权限控制:改用操作系统级隔离(容器、进程)、代码签名 + 运行时策略,或最小权限原则设计 API 边界 全局异常捕获:仍可用
AppDomain.CurrentDomain.UnhandledException—— 但注意:该事件在 .NET Core+ 中只是兼容保留,仅对主线程同步异常有效;推荐统一用
try/catch+
TaskScheduler.UnobservedTaskException+
Application.ThreadException(Windows Forms)组合覆盖
如果必须在 .NET Framework 中操作 AppDomain
仅限维护老项目,且明确运行于 full framework(如 Windows Server 2012 + .NET 4.8)。关键点:
不要直接 newAppDomain,用
AppDomain.CreateDomain("name")
跨域调用必须继承 MarshalByRefObject,否则会被序列化传值(导致类型加载失败或状态丢失) 卸载前确保无引用残留(尤其事件订阅、线程、Timer),否则
AppDomain.Unload()会阻塞甚至超时抛
CannotUnloadAppDomainException
AppDomainSetup.ApplicationBase必须设为绝对路径,否则加载程序集易失败
示例片段(仅作对照,勿复制到新项目):
var setup = new AppDomainSetup { ApplicationBase = @"C:\MyPlugins" };
var domain = AppDomain.CreateDomain("PluginDomain", null, setup);
domain.DoCallBack(() => Console.WriteLine("Running in isolated domain"));
AppDomain.Unload(domain); // 可能卡住,务必检查引用真正棘手的是卸载失败的排查——没有工具能自动追踪所有跨域引用,只能靠日志、弱引用检测和反复重启测试。
