一、反射(Reflection)的清晰定义
反射(Reflection) 是 .NET 框架提供的核心运行时机制,它允许程序在运行时而非编译时:
- 获取程序集(Assembly)、模块(Module)、类型(Type)的元数据信息
- 动态创建对象实例
- 动态调用方法
- 动态读取或修改属性/字段
- 动态解析自定义特性(Attribute)
对比理解
| 方式 | 特点 |
|---|---|
| 普通编码 | 编译期确定类型,直接调用,性能高 |
| 反射编程 | 运行期确定类型,动态操作,极度灵活 |
通俗理解
- 普通编程:编译期就确定要使用哪个类、调用哪个方法,代码是"写死的"。
- 反射编程:程序运行时才知道要操作哪个类,通过读取元数据"动态操作类型"。
👉 一句话:反射 = 运行时读取元数据 + 动态操作类型
二、.NET 中反射的底层基础
IL 与元数据
- C# → 编译 → IL(中间语言)
- 同时生成 Metadata(元数据表)(包含类型名、命名空间、成员签名、特性信息等)
反射的本质:
读取程序集中的 Metadata,而不是"反编译源码"
三、反射核心类继承体系与关键成员
1. 核心类继承结构
text
MemberInfo
├── FieldInfo
├── PropertyInfo
├── MethodBase
│ ├── ConstructorInfo
│ └── MethodInfo
└── EventInfo
MemberInfo是所有"成员描述类"的抽象基类。
2. 各核心类的关键成员(常用)
| 类名 | 核心父类 | 关键成员 | 作用说明 |
|---|---|---|---|
| MemberInfo | - | Name、GetCustomAttributes() | 获取成员名称、自定义特性 |
| MethodBase | MemberInfo | IsAbstract、IsPublic、IsStatic、Invoke() | 判断方法特性、调用方法或构造函数 |
| ConstructorInfo | MethodBase | Invoke(object[] parameters) | 调用构造函数创建实例 |
| MethodInfo | MethodBase | ReturnType、Invoke() | 获取返回值类型、调用方法 |
| PropertyInfo | MemberInfo | CanRead、CanWrite、PropertyType、GetValue()、SetValue() | 获取/设置属性值 |
四、Type 类深度解析(核心入口)
Type 是整个反射体系的入口类,几乎所有反射操作都从它开始。
常用成员
1. 类型判断
IsInterface、IsArray、IsPrimitive、IsEnumIsClass、IsPublic
2. 类型信息
Name(类型名)、FullName(完整类型名)、BaseType(基类)
3. 类型关系判断
IsInstanceOfType(object obj)IsAssignableFrom(Type type)
4. 成员获取
GetConstructor(Type[] paramTypes)GetMethod(string name, Type[] paramTypes)GetProperty(string name)
基础示例
csharp
Type type = typeof(Person);
Console.WriteLine(type.Name);
Console.WriteLine(type.FullName);
Console.WriteLine(type.BaseType);
Console.WriteLine(type.IsClass);
Console.WriteLine(type.IsPublic);
运行结果:
text
Person
ReflectionDemo.Person
System.Object
True
True
五、核心反射类型详解(附示例)
1. MemberInfo
csharp
Type type = typeof(Person);
foreach (MemberInfo member in type.GetMembers())
{
Console.WriteLine(member.Name + " - " + member.MemberType);
}
运行结果示例:
text
get_Name - Method
set_Name - Method
SayHi - Method
Name - Property
2. MethodInfo(方法反射)
获取并调用无参方法
csharp
MethodInfo method = type.GetMethod("SayHi");
method.Invoke(personObj, null);
运行结果:
text
Hi, my name is chen
调用带参数方法
csharp
MethodInfo method2 = type.GetMethod(
"SayHi",
new Type[] { typeof(string) }
);
method2.Invoke(personObj, new object[] { "Tom" });
运行结果:
text
Hi, Tom
3. ConstructorInfo(构造函数反射)
csharp
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
object obj = ctor.Invoke(null);
等价于:
csharp
object obj = Activator.CreateInstance(type);
4. PropertyInfo(属性反射)
csharp
PropertyInfo prop = type.GetProperty("Name");
// 设置值
prop.SetValue(obj, "chen");
// 获取值
string name = (string)prop.GetValue(obj);
输出:
text
chen
六、完整反射综合示例
csharp
using System;
using System.Reflection;
class Program
{
static void Main()
{
Type type = typeof(Person);
// 创建实例
object obj = Activator.CreateInstance(type);
// 设置属性
PropertyInfo prop = type.GetProperty("Name");
prop.SetValue(obj, "chen");
// 调用无参方法
MethodInfo m1 = type.GetMethod("SayHi");
m1.Invoke(obj, null);
// 调用带参方法
MethodInfo m2 = type.GetMethod(
"SayHi",
new[] { typeof(string) }
);
m2.Invoke(obj, new object[] { "Jack" });
}
}
class Person
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"Hi, my name is {Name}");
}
public void SayHi(string name)
{
Console.WriteLine($"Hi, {name}");
}
}
运行结果:
text
Hi, my name is chen
Hi, Jack
七、反射实现通用对象拷贝(完整 + 验证)
1. 拷贝方法(浅拷贝)
csharp
static object MyClone(object source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
Type type = source.GetType();
object target = Activator.CreateInstance(type);
foreach (PropertyInfo prop in type.GetProperties())
{
if (prop.CanRead && prop.CanWrite)
{
prop.SetValue(target, prop.GetValue(source));
}
}
return target;
}
2. 验证代码
csharp
Person p1 = new Person { Name = "chen", Age = 12 };
Person p2 = (Person)MyClone(p1);
p2.Name = "zhangsan";
Console.WriteLine(p1.Name);
Console.WriteLine(p2.Name);
运行结果:
text
chen
zhangsan
特点说明
- ✅ 支持任意对象、不依赖具体类型
- ❌ 仅实现浅拷贝
- ❌ 要求目标类型有无参构造函数
八、反射的典型使用场景(工程级)
1. 框架与底层组件
- ORM(EF Core、Dapper)
- 依赖注入(Autofac)
- 序列化(Newtonsoft.Json、System.Text.Json)
2. 插件化架构
- 动态加载 DLL
- 运行时发现并执行模块
3. 通用基础工具
- 对象拷贝
- 表单/模型验证
- 通用 Mapper
九、反射的代价、注意事项与优化方案
1. 性能问题
- 反射 ≈ 普通调用的 5~20 倍开销
- 优化方案:
- 缓存
Type / MethodInfo / PropertyInfo等反射对象 - 结合表达式树(Expression)优化
- 使用
Delegate.CreateDelegate转为委托调用
- 缓存
2. 封装性破坏
- 反射可绕过访问修饰符访问私有成员
- 建议:框架级场景使用,业务层慎用
3. 类型安全
- 编译期无法校验反射操作的类型正确性
- 必须做好异常处理(如
NullReferenceException、MissingMethodException等)
十、总结(工程视角)
- 反射是 .NET 框架能力的地基,是框架的灵魂
- 核心入口:
Type类;核心成员描述类:MethodInfo / PropertyInfo / ConstructorInfo - 优点:高度灵活、解耦,框架开发必备
- 缺点:性能损耗、类型不安全、破坏封装性
- 业务代码应"少而精"使用,框架代码必须掌握
反射不是为了偷懒,而是为了"抽象与解耦"。