AppDomain

AppDomain

在传统的 Windows 编程中,一个应用程序对应一个进程,进程之间是天然隔离的,一个进程崩了不会影响另一个。但这种隔离很"重",创建进程开销大,进程间通信也比较麻烦 。

.NET 引入 AppDomain 就是为了解决这个问题:它允许我们在一个进程内 创建多个隔离的运行环境,每个环境就叫一个 AppDomain。这样既享受了隔离带来的安全与稳定,又避免了开启多个进程的巨大开销 。

AppDomain 的三大核心特性

  1. 隔离性 (Isolation) :这是它最重要的特性。运行在不同 AppDomain 中的代码,其引用的类型、静态变量、甚至某些配置都是独立的。一个域里的代码抛出未处理异常而崩溃,通常不会影响进程中的其他 AppDomain

  2. 可卸载性 (Unloadable) :进程一旦启动,就无法卸载。但 AppDomain 可以。你可以创建一个新的域,在里面加载并执行一些程序集(比如一个插件),当不再需要时,可以完整地卸载掉整个 AppDomain,从而释放它占用的所有资源(包括那些原本无法卸载的程序集)。

  3. 可配置的安全性 :你可以为不同的 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"

  1. AppDomain.CurrentDomain:获取当前运行环境的引用。

  2. .BaseDirectory :这是核心属性。它返回的是 应用程序基目录

    • 特点 :它返回的字符串末尾通常已经带了一个反斜杠 \(但在某些环境或 .NET Core 中可能有微小差异)。

    • 现场优势 :无论你是双击运行、还是通过 Windows 服务启动,它始终指向 .exe 所在的文件夹。

  3. + @"\Config\DevConfig.xml"

    • @ 符号 :这是逐字字符串标识符。有了它,你就不需要写成 \\Config\\...(双斜杠转义),代码更简洁。

    • 路径拼接:将根目录与具体的配置子目录和文件名组合。

相关推荐
Nuopiane1 天前
关于C#/Unity中单例的探讨
java·jvm·c#
njsgcs1 天前
c# solidworks 获得视图的投影矩阵
矩阵·c#
进击的编程浪人1 天前
c/c++输入方法及对比
c语言·c++·c#
小曹要微笑1 天前
C#中的各种数据类型
算法·c#·数据类型·c#数据类型
曹牧1 天前
C#:控制函数执行时间
数据库·c#
小邓的技术笔记1 天前
C# 异步编程深水区:Task、ValueTask、线程池饥饿与背压设计
开发语言·c#
阿蒙Amon1 天前
C#常用类库-详解Dapper
开发语言·c#
猹叉叉(学习版)1 天前
【ASP.NET CORE】 6. 中间件
数据库·笔记·后端·中间件·c#·asp.net·.netcore
小邓的技术笔记1 天前
.NET 内存性能实战:Span<T>、ArrayPool、GC 与 LOH 控制
c#