Assembly 类位于 System.Reflection 命名空间,表示一个可重用、可部署且可版本化的 .NET 程序集。程序集是 .NET 应用程序的基本构建块,包含了类型、资源、元数据和清单。通过 Assembly 类,你可以加载程序集 、查询类型信息 、动态创建实例 、读取自定义特性 、访问嵌入资源等。
1. 如何获取 Assembly 对象
1.1 静态方法(常用)
| 方法 | 说明 |
|---|---|
| Assembly.GetExecutingAssembly() | 获取当前正在执行的代码所在的程序集。 |
| Assembly.GetCallingAssembly() | 获取调用当前方法的方法所在的程序集(性能稍差,慎用)。 |
| Assembly.GetEntryAssembly() | 获取应用程序的入口程序集(通常是可执行文件)。 |
| Assembly.Load() | 通过程序集的显示名称 或字节数组加载程序集。 |
| Assembly.LoadFrom() | 从指定文件路径加载程序集(会加载依赖项)。 |
| Assembly.LoadFile() | 仅加载指定路径的程序集,不自动加载依赖项。 |
| Assembly.ReflectionOnlyLoadFrom() | 仅用于反射(不可执行代码),.NET Core 5+ 不支持。 |
using System.Reflection;
// 1. 获取当前程序集
Assembly asm1 = Assembly.GetExecutingAssembly();
// 2. 通过名称加载(需已存在于应用程序域)
Assembly asm2 = Assembly.Load("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
// 3. 从文件加载
Assembly asm3 = Assembly.LoadFrom(@"C:\MyLibs\MyLibrary.dll");
// 4. 获取入口程序集
Assembly entryAsm = Assembly.GetEntryAssembly();
1.2 通过类型获取
csharp
Assembly asm4 = typeof(MyClass).Assembly;
2. 常用属性
| 属性 | 说明 |
|---|---|
| FullName | 程序集完整显示名称(包含名称、版本、区域、公钥令牌)。 |
| Location | 程序集文件在磁盘上的物理路径(若已加载)。 |
| CodeBase | 程序集的原始 URI 位置(已过时,建议用 Location)。 |
| ImageRuntimeVersion | 程序集生成时使用的 CLR 版本(如 v4.0.30319)。 |
| EntryPoint | 返回程序集入口方法(如 Main 方法),若无则返回 null。 |
| IsDynamic | 指示程序集是否通过反射发出(Reflection.Emit)动态生成。 |
csharp
Console.WriteLine(asm1.FullName); // "MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Console.WriteLine(asm1.Location); // "C:\MyApp\bin\Debug\MyApp.exe"
Console.WriteLine(asm1.ImageRuntimeVersion);// "v4.0.30319"
3. 常用方法
3.1 类型反射
| 方法 | 说明 |
|---|---|
| GetTypes() | 获取程序集中定义的所有公共类型。 |
| GetExportedTypes() | 获取程序集中定义的公共类型(可被外部访问)。 |
| GetType(string name) | 通过类型全名获取 Type 对象。 |
| GetReferencedAssemblies() | 获取当前程序集引用的所有程序集名称(AssemblyName)。 |
Type[] allTypes = asm1.GetTypes();
foreach (Type t in allTypes)
{
Console.WriteLine(t.FullName);
}
Type myType = asm1.GetType("MyNamespace.MyClass");
3.2 动态创建实例
| 方法 | 说明 |
|---|---|
| CreateInstance(string typeName) | 创建指定类型(区分大小写)的实例,返回 object。 |
| CreateInstance(string typeName, bool ignoreCase) | 是否忽略大小写。 |
| CreateInstance(string typeName, bool ignoreCase, BindingFlags binder, object[] args, CultureInfo culture) | 完整重载,支持参数传递。 |
// 创建无参实例
object obj = asm1.CreateInstance("MyNamespace.MyClass");
// 创建带参数的实例
object obj2 = asm1.CreateInstance(
"MyNamespace.MyClass",
false,
BindingFlags.Default,
null,
new object[] { 42, "hello" },
null,
null);
3.3 读取自定义特性
| 方法 | 说明 |
|---|---|
| GetCustomAttributes() | 获取程序集上定义的所有自定义特性。 |
| GetCustomAttributes(Type attributeType) | 获取指定类型的特性。 |
| GetCustomAttributesData() | 获取特性数据(不创建实例,适用于仅反射场景)。 |
// 获取程序集的 AssemblyTitle 特性
var titleAttr = asm1.GetCustomAttribute<AssemblyTitleAttribute>();
Console.WriteLine(titleAttr?.Title);
3.4 访问嵌入资源
| 方法 | 说明 |
|---|---|
| GetManifestResourceNames() | 获取程序集中所有嵌入资源的名称。 |
| GetManifestResourceStream(string name) | 获取嵌入资源的数据流(Stream)。 |
| GetManifestResourceInfo(string name) | 获取资源的位置等信息。 |
csharp
// 列出所有嵌入资源
string[] resources = asm1.GetManifestResourceNames(); //获取程序集中所有嵌入资源的名称
foreach (string res in resources)
{
Console.WriteLine(res);
}
// 读取文本资源
using (Stream stream = asm1.GetManifestResourceStream("MyApp.Resources.Config.xml"))
using (StreamReader reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
}
注意 :资源名称通常是命名空间.文件夹名.文件名,需准确指定。
3.5 安全性
| 方法 | 说明 |
|---|---|
| Evidence | 获取程序集的证据(用于安全策略,已过时)。 |
| PermissionSet | 获取程序集所需的权限集。 |
4. 完整示例:加载外部程序集并调用方法
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
// 1. 从文件加载程序集
Assembly pluginAsm = Assembly.LoadFrom(@"C:\Plugins\Calculator.dll");
// 2. 获取类型
Type calcType = pluginAsm.GetType("CalculatorLib.Calculator");
if (calcType == null)
{
Console.WriteLine("类型未找到");
return;
}
// 3. 创建实例
object calculator = Activator.CreateInstance(calcType);
// 或使用 assembly.CreateInstance:
// object calculator = pluginAsm.CreateInstance("CalculatorLib.Calculator");
// 4. 调用方法
MethodInfo addMethod = calcType.GetMethod("Add", new Type[] { typeof(int), typeof(int) });
int result = (int)addMethod.Invoke(calculator, new object[] { 3, 5 });
Console.WriteLine($"3 + 5 = {result}");
// 5. 读取程序集特性
var versionAttr = pluginAsm.GetCustomAttribute<AssemblyFileVersionAttribute>();
Console.WriteLine($"插件版本: {versionAttr?.Version}");
}
}
5. 注意事项
-
性能 :反射(特别是
Invoke)比直接调用慢得多,应避免在性能敏感的循环中使用。 -
依赖项 :使用
LoadFrom时,CLR 会尝试加载依赖程序集(从相同目录或全局程序集缓存)。如果依赖项不在预期位置,会引发FileNotFoundException。 -
多个上下文 :
Load与LoadFrom将程序集加载到不同的上下文,可能导致同一程序集被加载两次。 -
安全性 :从不受信任的源加载程序集存在安全风险(可执行任意代码),建议使用
AssemblyLoadContext(.NET Core 3+)隔离。 -
动态程序集 :
AssemblyBuilder生成的动态程序集无法保存到文件(除非指定了RunAndSave或Save)。 -
.NET Core/.NET 5+ 变更:
-
Assembly.CodeBase和Assembly.EscapedCodeBase已过时,改用Assembly.Location。 -
Assembly.LoadFile在 .NET Core 中行为不同(直接加载,无依赖解析)。 -
推荐使用
AssemblyLoadContext进行高级程序集加载控制。
-
6. 总结
Assembly 类是 .NET 反射机制的核心入口之一。掌握它,你就能动态地探索和操作程序集,实现插件系统、依赖注入、模块化设计、资源提取等功能。关键要理解如何获取程序集对象、查询元数据、创建实例和访问嵌入资源,同时注意性能和安全陷阱。
通过 Assembly 类,你几乎可以获取关于程序集的一切信息,是 .NET 高级编程的必备技能。
代码文件"与"运行进程"**的关系:
| 属性 | 定义 | 现场实操中的含义 |
|---|---|---|
Assembly.GetExecutingAssembly().Location |
当前代码程序集(.dll 或 .exe)所在的磁盘绝对路径。 | 它是静态的"家"。你通过哪个文件启动,它就指哪里。 |
currentProcess.MainModule.FileName |
当前操作系统进程对应的可执行文件路径。 | 它是动态的"身份证明"。Windows 记录了这个进程是从哪个 .exe 运行起来的。 |