AppDomain
在传统的 Windows 编程中,一个应用程序对应一个进程,进程之间是天然隔离的,一个进程崩了不会影响另一个。比如,你可以创建一个“沙箱域”来运行从网络下载的不受信任的插件,限制它不能读写硬盘、不能访问注册表,从而保护主程序的安全。:虽然比跨进程通信快,但通过代理跨域调用对象方法,仍然比直接调用有性能损耗,因为涉及参数和返回值的序列化/反序列化(按引用传递的对象除外)。你可以创建一个新的域,在里面
AppDomain
在传统的 Windows 编程中,一个应用程序对应一个进程,进程之间是天然隔离的,一个进程崩了不会影响另一个。但这种隔离很“重”,创建进程开销大,进程间通信也比较麻烦 。
.NET 引入 AppDomain 就是为了解决这个问题:它允许我们在一个进程内创建多个隔离的运行环境,每个环境就叫一个 AppDomain。这样既享受了隔离带来的安全与稳定,又避免了开启多个进程的巨大开销 。
AppDomain 的三大核心特性
-
隔离性 (Isolation):这是它最重要的特性。运行在不同
AppDomain中的代码,其引用的类型、静态变量、甚至某些配置都是独立的。一个域里的代码抛出未处理异常而崩溃,通常不会影响进程中的其他AppDomain。 -
可卸载性 (Unloadable):进程一旦启动,就无法卸载。但
AppDomain可以。你可以创建一个新的域,在里面加载并执行一些程序集(比如一个插件),当不再需要时,可以完整地卸载掉整个 AppDomain,从而释放它占用的所有资源(包括那些原本无法卸载的程序集)。 -
可配置的安全性:你可以为不同的
AppDomain设置不同的权限集。比如,你可以创建一个“沙箱域”来运行从网络下载的不受信任的插件,限制它不能读写硬盘、不能访问注册表,从而保护主程序的安全 。
AppDomain 的常用成员
AppDomain 类在 System 命名空间下,下面这些属性和方法是你最需要掌握的
✨ 常用属性
| 属性名 | 作用说明 | 使用场景 |
|---|---|---|
| CurrentDomain | 静态属性,获取当前线程正在执行的 AppDomain 对象 。 |
这是你最常用的入口,比如 AppDomain.CurrentDomain.GetAssemblies()。 |
| FriendlyName | 获取该 AppDomain 的友好名称,便于识别 。 |
默认域的友好名通常是程序集文件名(如 MyApp.exe);自定义域的名称由你在创建时指定。 |
| BaseDirectory | 获取程序集探测的基目录,通常是应用程序的根目录 。 | 用于定位和加载与应用程序同目录的程序集。 |
| SetupInformation | 获取该域的配置信息(如配置文件路径、影子复制设置等)。 | 当你需要了解一个 AppDomain 是如何被初始化的时候。 |
⚙️ 常用方法
| 方法名 | 作用说明 | 使用场景 |
|---|---|---|
| CreateDomain() | 静态方法,创建一个新的应用程序域 。 | 用于创建插件域、沙箱域等。你可以指定名称、安全性证据、配置信息等。 |
| Unload() | 静态方法,卸载指定的应用程序域 。 | 当不再需要某个域时,调用此方法彻底清理其占用的资源。 |
| GetAssemblies() | 获取已加载到此应用程序域中的所有程序集 。 | 用于反射遍历当前域加载了哪些 DLL,常用于实现插件发现或属性扫描。 |
| CreateInstanceAndUnwrap() | 在指定程序集中创建类型实例,并返回一个透明的代理对象 。 | 这是跨域通信的核心方法。用于在“当前域”中创建并操作“另一个域”里的对象。 |
| Load() | 将程序集加载到此应用程序域中 。 | 手动加载一个程序集到当前域。 |
| DoCallBack() | 在另一个应用程序域中执行一个委托指定的代码 。 | 当你想让目标域直接执行一些简单代码,而不需要创建复杂对象时。 |
| SetData() / GetData() | 为应用程序域属性分配/获取值 。 | 一种简单的跨域共享数据的方式(数据会被序列化传递)。 |
📢 常用事件
| 事件名 | 触发时机 | 使用场景 |
|---|---|---|
| AssemblyLoad | 当程序集被加载到该域时发生 。 | 用于监控域中加载了哪些程序集。 |
| AssemblyResolve | 当 CLR 解析程序集失败时发生 。 | 用于实现高级的程序集加载逻辑,例如从特定路径或内存中加载程序集。 |
| UnhandledException | 当域中有异常未被捕获时发生 。 | 这是一个全局的异常捕获点,用于记录日志或执行最后的清理工作。 |
| DomainUnload | 当 AppDomain 即将被卸载时发生 。 |
用于在域卸载前进行一些清理工作,比如关闭文件句柄、断开网络连接等。 |
| ProcessExit | 当默认域的父进程退出时发生 。 | 应用程序关闭前最后的清理机会。 |
⚠️ 重要提示与现状
-
程序集无法单独卸载:这是 .NET 的一个基本设计。一旦一个程序集(DLL)被加载到
AppDomain中,就无法单独卸载它。如果你想释放这个程序集,唯一的办法就是卸载整个 AppDomain 。 -
跨域通信有代价:虽然比跨进程通信快,但通过代理跨域调用对象方法,仍然比直接调用有性能损耗,因为涉及参数和返回值的序列化/反序列化(按引用传递的对象除外)。
-
对象传递方式:在域间传递对象时,如果类型继承自
MarshalByRefObject,则通过代理按引用传递;否则,对象会被按值序列化(即复制一份到目标域)。你的PluginHost必须继承MarshalByRefObject。 -
在 .NET Core 和 .NET 5/6/7/8+ 中的变化:
AppDomain的概念仍然存在,且是运行时的基石。但为了跨平台支持和简化,创建和卸载额外的 AppDomain 的功能在新的 .NET Core 版本中受到很大限制,并且不推荐作为跨平台代码隔离的主要手段。在大多数新的 .NET (Core) 应用程序中,你很少需要自己创建额外的AppDomain。如果你有插件或代码隔离的需求,微软现在更推荐使用 AssemblyLoadContext 作为替代方案,它提供了更现代化和可控的程序集加载与卸载能力 。
代码解释:
AppDomain.CurrentDomain.BaseDirectory + @"\Config\DevConfig.xml"
AppDomain.CurrentDomain.BaseDirectory + @"\Config\DevConfig.xml"
-
AppDomain.CurrentDomain:获取当前运行环境的引用。 -
.BaseDirectory:这是核心属性。它返回的是 应用程序基目录。-
特点:它返回的字符串末尾通常已经带了一个反斜杠
\(但在某些环境或 .NET Core 中可能有微小差异)。 -
现场优势:无论你是双击运行、还是通过 Windows 服务启动,它始终指向
.exe所在的文件夹。
-
-
+ @"\Config\DevConfig.xml":-
@符号:这是逐字字符串标识符。有了它,你就不需要写成\\Config\\...(双斜杠转义),代码更简洁。 -
路径拼接:将根目录与具体的配置子目录和文件名组合。
-
更多推荐



所有评论(0)