C# Assembly

💡 原文中文,约6000字,阅读约需15分钟。
📝

内容提要

Assembly是.NET框架的基本构建模块,用于封装代码和资源,并支持版本控制、安全性、类型信息、可再分发性和依赖管理。私有Assembly仅供单个应用程序使用,共享Assembly可以被多个应用程序使用。GAC是全局程序集缓存,用于存储共享Assembly。Assembly和AppDomain是不同的概念,Assembly是基本构建单元,用于模块化和重用,而AppDomain是应用程序的执行环境,提供隔离和管理。CLR不提供卸载单独程序集的能力,因为会导致应用程序崩溃,可以通过卸载整个AppDomain来释放相关的程序集和资源。反射的缺点包括类型安全性不确定、速度慢和性能影响,应避免使用反射来访问字段或调用方法/属性。

🔎

延伸解读

Assembly 与 AppDomain:物理单元与逻辑环境的区分

Assembly 是代码和资源的物理单位,以 DLL 或 EXE 形式存在,承载类型信息和版本控制;AppDomain 是逻辑执行环境,提供进程内隔离。Assembly 加载后共享到 AppDomain 中,而 AppDomain 用于隔离和管理执行。理解这一区别有助于在模块化设计和运行时隔离之间做出正确选择。

为何 CLR 不支持卸载单个程序集

CLR 不提供卸载单独程序集的能力,因为一旦线程返回到已卸载程序集中的代码,应用程序就会崩溃。即使通过 ReflectionOnlyLoadFrom 加载的程序集表面可卸载,CLR 也不允许,因为仍可能通过反射创建对象引用其元数据。卸载程序集必须卸载整个 AppDomain,这是为了确保应用程序的稳定性和安全性。

反射的性能代价与替代方案

反射依赖字符串进行类型和成员查找,丧失编译时类型安全性,且运行时字符串搜索和参数打包解包会显著影响性能。示例中直接调用百万次仅需 1 毫秒,反射调用则需 10 毫秒。建议避免用反射访问字段或调用方法,可让类型从已知基类派生或实现已知接口,在运行时构造实例并通过基类或接口调用。

AppDomain.Load 的潜在风险与使用建议

AppDomain.Load 是实例方法,设计由非托管代码调用,用于将程序集注入指定 AppDomain。托管代码一般不应调用,因为程序集对象需按值封送回发出调用的 AppDomain,CLR 会使用发出调用方的策略和搜索位置重新加载,可能导致 FileNotFoundException 或加载非预期文件。应优先使用 Assembly.Load 进行动态加载。

Q&A

Assembly在.NET框架中有什么作用?

Assembly是.NET框架的基本构建模块,封装代码和资源,支持版本控制、安全性、类型信息、可再分发性和依赖管理。

私有Assembly和共享Assembly有什么区别?

私有Assembly仅供单个应用程序使用,通常存放在应用程序目录中;共享Assembly可以被多个应用程序使用,通常存放在全局程序集缓存(GAC)中。

全局程序集缓存(GAC)有什么重要特点?

GAC允许多个应用程序共享使用公共的Assembly,支持不同版本的共存,并且只有具有强名称签名的Assembly才能存放在GAC中。

Assembly和AppDomain之间的区别是什么?

Assembly是代码和资源的物理单位,而AppDomain是逻辑的执行环境,AppDomain提供应用程序的隔离和管理。

为什么CLR不支持卸载单独的程序集?

CLR不支持卸载单独程序集是为了确保应用程序的稳定性和安全性,卸载单个程序集可能导致内存管理复杂性和应用程序崩溃。

反射在C#中有哪些缺点?

反射的缺点包括类型安全性不确定、速度慢和性能影响,建议避免使用反射来访问字段或调用方法/属性。

🏷️

标签

➡️

继续阅读