Reflection即反射,是.NET框架提供的一组用于在运行时检查和管理程序集、模块、类型以及类型成员的能力。它打破了编译时类型绑定的限制,让程序可以在运行阶段动态获取类型信息、调用方法、创建实例,是很多灵活架构设计的基础。动态加载类型则是反射技术的常见用途,能够让程序在不需要重新编译的情况下,加载外部的DLL文件并使用其中的类型。

Reflection的核心概念
Reflection相关的核心类都位于System.Reflection命名空间下,其中最常用的是Assembly、Type、MethodInfo等类型。
Assembly类
Assembly表示一个程序集,是.NET中部署和版本控制的基本单位,一个DLL或者EXE文件就对应一个Assembly实例。通过Assembly可以获取程序集包含的所有类型、模块等信息。
Type类
Type类表示类型声明,包括类、接口、结构体、枚举等。通过Type可以获取类型的名称、命名空间、继承关系、包含的方法、属性、字段等信息,是反射操作的核心入口。
成员信息类
除了上述两个核心类,还有MethodInfo(表示方法)、PropertyInfo(表示属性)、FieldInfo(表示字段)、ConstructorInfo(表示构造函数)等类,用于获取和操作类型的各个成员。
Reflection的常见使用场景
- 插件化开发:主程序在运行时加载外部插件DLL,动态调用插件提供的功能,不需要在编译时依赖插件代码。
- 配置化功能:根据配置文件中的类型名称,动态创建对应的实例,实现功能的热切换。
- 对象序列化/反序列化:很多序列化框架通过反射获取对象的属性和字段,完成数据的转换。
- 依赖注入框架:框架通过反射扫描程序集,自动注册和解析依赖的类型。
动态加载类型的实现步骤
动态加载类型的核心流程是:加载目标程序集,从程序集中获取指定类型,创建类型实例,调用类型的方法或访问成员。下面通过一个完整的示例来演示整个过程。
步骤1:准备待加载的外部程序集
首先我们创建一个类库项目,定义一个简单的接口和实现类,编译生成DLL文件供主程序加载。类库代码如下:
using System;
namespace ExternalLib
{
// 定义公共接口,方便主程序依赖接口而不是具体实现
public interface IExternalService
{
string SayHello(string name);
}
// 接口的实现类
public class ExternalService : IExternalService
{
public string SayHello(string name)
{
return $"Hello, {name}! This is from external assembly.";
}
}
}
将上述代码编译后,得到ExternalLib.dll文件,放到主程序的运行目录下。
步骤2:主程序加载程序集
主程序通过Assembly.LoadFrom或者Assembly.LoadFile方法加载外部的DLL文件。两者的区别是LoadFrom会加载DLL的依赖项,而LoadFile只会加载指定的文件。一般推荐使用LoadFrom。
using System;
using System.Reflection;
namespace ReflectionDemo
{
class Program
{
static void Main(string[] args)
{
// 加载外部程序集,路径可以是绝对路径或者相对于程序运行目录的相对路径
string assemblyPath = "ExternalLib.dll";
Assembly externalAssembly = Assembly.LoadFrom(assemblyPath);
Console.WriteLine($"加载程序集成功:{externalAssembly.FullName}");
}
}
}
步骤3:从程序集中获取目标类型
加载程序集后,可以通过Assembly.GetType方法获取程序集中的指定类型,需要传入类型的完全限定名(命名空间+类名)。
using System;
using System.Reflection;
namespace ReflectionDemo
{
class Program
{
static void Main(string[] args)
{
string assemblyPath = "ExternalLib.dll";
Assembly externalAssembly = Assembly.LoadFrom(assemblyPath);
// 获取目标类型的完全限定名
string typeFullName = "ExternalLib.ExternalService";
Type targetType = externalAssembly.GetType(typeFullName);
if (targetType == null)
{
Console.WriteLine("未找到目标类型");
return;
}
Console.WriteLine($"找到目标类型:{targetType.FullName}");
}
}
}
步骤4:创建类型实例并调用方法
获取到Type实例后,可以通过Activator.CreateInstance方法创建类型的实例,然后通过反射调用实例的方法。如果类型实现了公共接口,也可以将实例转换为接口类型来调用方法,避免直接依赖具体类型。
using System;
using System.Reflection;
namespace ReflectionDemo
{
// 主程序中也定义相同的接口,保证类型兼容,实际开发中可以将接口放到独立的公共类库中
public interface IExternalService
{
string SayHello(string name);
}
class Program
{
static void Main(string[] args)
{
string assemblyPath = "ExternalLib.dll";
Assembly externalAssembly = Assembly.LoadFrom(assemblyPath);
string typeFullName = "ExternalLib.ExternalService";
Type targetType = externalAssembly.GetType(typeFullName);
if (targetType == null)
{
Console.WriteLine("未找到目标类型");
return;
}
// 创建类型实例
object instance = Activator.CreateInstance(targetType);
// 转换为接口类型,方便调用方法
IExternalService service = instance as IExternalService;
if (service == null)
{
Console.WriteLine("类型未实现IExternalService接口");
return;
}
// 调用接口方法
string result = service.SayHello("Reflection");
Console.WriteLine($"调用结果:{result}");
// 也可以通过反射直接获取方法并调用,不需要转换接口
MethodInfo sayHelloMethod = targetType.GetMethod("SayHello");
if (sayHelloMethod != null)
{
object methodResult = sayHelloMethod.Invoke(instance, new object[] { "Direct Reflection" });
Console.WriteLine($"直接反射调用结果:{methodResult}");
}
}
}
}
动态加载类型的注意事项
- 程序集路径问题:加载程序集时需要确保路径正确,相对路径是相对于程序当前的运行目录,而不是代码文件所在目录。
- 类型完全限定名:获取类型时必须使用正确的完全限定名,包括命名空间,否则会返回null。
- 依赖项问题:如果外部程序集依赖其他DLL,需要将这些依赖项也放到程序运行目录下,否则加载程序集时会抛出异常。
- 异常处理:动态加载过程中可能出现文件不存在、类型不存在、依赖缺失等异常,需要做好异常捕获和处理。
- 性能问题:反射操作的性能比直接编译时调用要低,如果对性能要求较高的场景,可以考虑使用缓存Type实例、使用表达式树生成委托等方式优化。
反射的其他常用操作
除了动态加载类型和创建实例,反射还支持很多其他操作,例如:
获取类型的所有方法
using System;
using System.Reflection;
namespace ReflectionDemo
{
class Program
{
static void Main(string[] args)
{
Type stringType = typeof(string);
MethodInfo[] methods = stringType.GetMethods();
Console.WriteLine($"string类型共有{methods.Length}个方法:");
foreach (MethodInfo method in methods)
{
Console.WriteLine($"方法名:{method.Name},返回类型:{method.ReturnType.Name}");
}
}
}
}
访问对象的属性和字段
using System;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age;
}
class Program
{
static void Main(string[] args)
{
User user = new User { Name = "张三", Age = 25 };
Type userType = typeof(User);
// 访问属性
PropertyInfo nameProperty = userType.GetProperty("Name");
if (nameProperty != null)
{
string nameValue = nameProperty.GetValue(user) as string;
Console.WriteLine($"Name属性值:{nameValue}");
// 设置属性值
nameProperty.SetValue(user, "李四");
Console.WriteLine($"修改后Name属性值:{user.Name}");
}
// 访问字段
FieldInfo ageField = userType.GetField("Age");
if (ageField != null)
{
int ageValue = (int)ageField.GetValue(user);
Console.WriteLine($"Age字段值:{ageValue}");
// 设置字段值
ageField.SetValue(user, 30);
Console.WriteLine($"修改后Age字段值:{user.Age}");
}
}
}
}
反射是.NET框架中非常强大的功能,合理使用可以大幅提升程序的灵活性,但也要注意其性能开销和使用场景,避免过度使用导致代码可读性和性能下降。
Reflection动态加载类型AssemblyTypeSystem_Reflection修改时间:2026-06-22 00:42:23