C# 反射(Reflection)详解
反射(Reflection)是 C# 中一种动态分析程序集、类型及成员的机制,允许在运行时获取类型信息、创建对象、调用方法或访问字段,无需在编译时明确知道具体类型。
一、反射的核心功能
1、动态类型操作
- 获取类型信息(类、接口、结构体等)。
- 实例化对象、调用方法、读写字段/属性。
2、程序集分析
- 加载外部程序集(DLL),遍历其包含的类型和成员。
3、元数据访问
- 读取特性(Attribute)、泛型参数、方法签名等元数据。
二、核心类与用法
1、System.Type 类
获取类型的途径:
// 通过对象获取
Type type1 = obj.GetType();
// 通过类型名获取
Type type2 = typeof(int);
Type type3 = Type.GetType("System.String");
// 通过程序集获取
Assembly assembly = Assembly.Load("MyLibrary");
Type type4 = assembly.GetType("MyLibrary.MyClass");
常用方法:
- GetMethods():获取所有公共方法。
- GetProperties():获取所有属性。
- GetCustomAttributes():读取特性。
2、System.Reflection.Assembly 类
- 加载程序集:
// 从文件加载
Assembly asm1 = Assembly.LoadFrom("MyLibrary.dll");
// 通过程序集名加载
Assembly asm2 = Assembly.Load("MyLibrary");
- 遍历程序集中的类型:
foreach (Type type in asm1.GetTypes())
{
Console.WriteLine(type.FullName);
}
3、Activator 类
- 动态创建对象:
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type);
// 带参数的构造函数
object instance2 = Activator.CreateInstance(type, "参数1", 100);
4、MethodInfo 与调用方法
Type type = typeof(MyClass);
MethodInfo method = type.GetMethod("MyMethod");
object instance = Activator.CreateInstance(type);
// 调用无参方法
method.Invoke(instance, null);
// 调用有参方法
method.Invoke(instance, new object[] { "参数", 42 });
三、典型应用场景
1、动态插件系统
- 加载外部 DLL,实现模块化扩展。
Assembly pluginAsm = Assembly.LoadFrom("Plugin.dll");
Type pluginType = pluginAsm.GetType("Plugin.MyPlugin");
IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
plugin.Execute();
2、依赖注入(DI)框架
- 通过反射自动解析构造函数参数并实例化服务。
public static T Resolve<T>()
{
Type type = typeof(T);
ConstructorInfo ctor = type.GetConstructors().First();
ParameterInfo[] paramsInfo = ctor.GetParameters();
object[] args = paramsInfo.Select(p => Resolve(p.ParameterType)).ToArray();
return (T)ctor.Invoke(args);
}
3、序列化与反序列化
- 动态读取对象的字段/属性并转换为 JSON 或 XML。
public static string ToJson(object obj)
{
var sb = new StringBuilder("{");
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
object value = prop.GetValue(obj);
sb.Append($"\"{prop.Name}\":\"{value}\",");
}
sb.Remove(sb.Length - 1, 1).Append("}");
return sb.ToString();
}
4、ORM(对象关系映射)
- 将数据库查询结果映射到实体类。
public static T MapToEntity<T>(DataRow row) where T : new()
{
T entity = new T();
foreach (DataColumn column in row.Table.Columns)
{
PropertyInfo prop = typeof(T).GetProperty(column.ColumnName);
if (prop != null && row[column] != DBNull.Value)
prop.SetValue(entity, row[column]);
}
return entity;
}
四、性能与优化
1、反射的性能问题
- 反射操作(如 Invoke)比直接代码调用慢 10~100 倍。
- 频繁使用反射可能导致性能瓶颈。
2、优化策略
- 缓存反射结果:
private static readonly MethodInfo _cachedMethod = typeof(MyClass).GetMethod("MyMethod");
- 使用 Delegate 或 Expression:
// 将 MethodInfo 转换为委托
Action<object, object[]> methodDelegate = (Action<object, object[]>)
Delegate.CreateDelegate(typeof(Action<object, object[]>), _cachedMethod);
- 预编译表达式树:
var param = Expression.Parameter(typeof(MyClass));
var call = Expression.Call(param, _cachedMethod);
var lambda = Expression.Lambda<Action<MyClass>>(call, param).Compile();
lambda(obj); // 高速调用
五、反射的局限性
1、安全性限制
- 在部分受信任环境(如沙箱)中,反射可能被限制。
2、破坏封装性 - 反射可访问私有成员,过度使用可能导致代码脆弱性。
3、类型强依赖 - 动态代码若类型不匹配,会引发运行时异常(而非编译错误)。
总结
C# 反射是处理动态类型和元数据的强大工具,广泛应用于插件系统、序列化、ORM 等场景。尽管其灵活性极高,但需谨慎使用以避免性能问题和代码维护困难。优化策略(如缓存、表达式树)和合理设计(如接口隔离)是高效使用反射的关键。