反射(Reflection)是 C# 中最重要的高级技术之一,也是很多框架(如 ASP.NET Core、Entity Framework、Unity、依赖注入、ORM)工作的基础。
一句话理解:
反射就是程序在运行时,查看自己、分析自己、甚至修改自己的能力。
例如:
- 查看一个类有哪些属性
- 查看有哪些方法
- 动态创建对象
- 动态调用方法
- 动态读取属性
- 动态加载DLL
一、为什么需要反射?
正常情况下,我们写代码都是固定的。
例如:
cs
Student stu = new Student();
stu.Name = "张三";
stu.SayHello();
这里编译器已经知道:
- Student类
- Name属性
- SayHello方法
这些都在编译阶段确定。
但是如果:
今天程序运行的时候才知道类叫什么?
例如数据库里存着:
Student
Teacher
Dog
Cat
程序需要根据字符串创建对象。
这时候:
new Student();
已经写不了了。
怎么办?
就需要:
反射(Reflection)
二、什么是反射
反射就是:
运行时获取类型(Type)的信息。
例如:
一个Student类
cs
class Student
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello()
{
Console.WriteLine("你好");
}
}
利用反射:
可以得到:
类名
Student
属性
Name
Age
方法
SayHello
ToString
Equals
...
这些信息都可以获取。
三、反射核心类
反射主要在:
System.Reflection
命名空间。
最重要几个类:
cs
Assembly 程序集(DLL、EXE)
Type 类型
ConstructorInfo 构造函数
MethodInfo 方法
PropertyInfo 属性
FieldInfo 字段
EventInfo 事件
真正最核心的是:
Type
四、获取Type对象
有三种方法。
第一种:typeof()
Type type = typeof(Student);
输出:
Student
这是最常用的方法。
第二种:GetType()
对象获取类型。
cs
Student stu = new Student();
Type type = stu.GetType();
得到:
Student
第三种:Type.GetType()
cs
Type type = Type.GetType("ConsoleApp.Student");
注意:
必须写完整命名空间。
例如:
ConsoleApp.Student
否则找不到。
五、获取类的信息
例如:
Type type = typeof(Student);
获取名称:
cs
Console.WriteLine(type.Name);
输出:
Student
获取完整名称:
cs
Console.WriteLine(type.FullName);
输出:
ConsoleApp.Student
获取命名空间:
cs
Console.WriteLine(type.Namespace);
输出:
cs
ConsoleApp
六、获取所有属性
例如:
cs
PropertyInfo[] properties = type.GetProperties();
遍历:
cs
foreach (PropertyInfo p in properties)
{
Console.WriteLine(p.Name);
}
输出:
Name
Age
获取属性类型:
cs
Console.WriteLine(p.PropertyType);
输出:
cs
System.String
System.Int32
七、获取字段(Field)
如果是字段:
cs
public string Name;
public int Age;
使用:
FieldInfo[] fields = type.GetFields();
遍历:
cs
foreach(FieldInfo f in fields)
{
Console.WriteLine(f.Name);
}
八、获取方法
获取所有方法:
MethodInfo[] methods = type.GetMethods();
遍历:
cs
foreach(MethodInfo m in methods)
{
Console.WriteLine(m.Name);
}
输出:
cs
SayHello
Equals
GetHashCode
ToString
因为:
父类Object的方法也会显示。
九、获取构造函数
ConstructorInfo[] constructors = type.GetConstructors();
遍历:
输出:
Student
十、利用反射创建对象
正常:
Student stu = new Student();
反射:
cs
Type type = typeof(Student);
object obj = Activator.CreateInstance(type);
这里:
obj
就是Student对象。
可以转换:
Student stu = (Student)obj;
如果有构造函数:
cs
public Student(string name)
{
Name = name;
}
创建:
object obj = Activator.CreateInstance(type,"张三");
十一、调用方法
例如:
cs
class Student
{
public void SayHello()
{
Console.WriteLine("你好");
}
}
反射:
cs
Type type = typeof(Student);
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(obj, null);
输出:
你好
有参数:
cs
public void Add(int a,int b)
{
Console.WriteLine(a+b);
}
调用:
cs
MethodInfo method = type.GetMethod("Add");
method.Invoke(obj,new object[]{3,5});
输出:
8
十二、读取属性
例如:
cs
Student stu = new Student();
stu.Name="张三";
反射:
cs
PropertyInfo property = type.GetProperty("Name");
Console.WriteLine(property.GetValue(stu));
输出:
张三
修改属性:
property.SetValue(stu,"李四");
输出:
李四
十三、动态调用(最经典)
例如:
数据库存:
cs
ClassName
Student
方法:
SayHello
代码:
cs
string className = "ConsoleApp.Student";
Type type = Type.GetType(className);
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(obj,null);
程序运行后:
你好
完全没有写:
new Student();
十四、加载DLL
例如:
插件:
Plugin.dll
加载:
cs
Assembly assembly = Assembly.LoadFrom("Plugin.dll");
获取类型:
cs
Type type = assembly.GetType("Plugin.Test");
创建对象:
cs
object obj = Activator.CreateInstance(type);
调用方法:
cs
type.GetMethod("Run").Invoke(obj,null);
这就是插件开发最基本原理。
十五、反射性能问题
反射为什么慢?
普通调用:
stu.SayHello();
CPU直接知道地址。
反射:
字符串
↓
找Type
↓
找Method
↓
检查参数
↓
Invoke
↓
执行
中间多了很多步骤。
一般:
普通调用
100万次
≈5ms
反射
100万次
≈150ms~500ms
大约慢 20~100 倍(具体取决于运行环境和调用方式)。
因此:
不要在高频循环中频繁使用反射。
可以缓存 Type、MethodInfo,甚至结合委托来减少开销。
十六、反射的实际应用
几乎所有大型 C# 项目都会使用反射,例如:
| 场景 | 是否使用反射 | 作用 |
|---|---|---|
| ASP.NET Core | ✅ | 自动发现控制器、Action |
| Entity Framework Core | ✅ | 映射实体与数据库 |
| JSON 序列化(如 System.Text.Json) | ✅ | 读取对象属性进行序列化/反序列化 |
| 依赖注入(DI) | ✅ | 自动创建对象、注入依赖 |
| 插件系统 | ✅ | 动态加载 DLL 并调用功能 |
| Unity | ✅ | 查找带有特性的脚本、编辑器扩展 |
十七、反射学习路线
建议按以下顺序掌握:
- Type
- 获取类型信息
- 类型判断
- PropertyInfo
- 获取属性
- 读取与设置属性值
- MethodInfo
- 查找并调用方法
- 传递参数、处理返回值
- FieldInfo
- 访问字段
- ConstructorInfo
- 调用不同构造函数创建对象
- Assembly
- 加载 DLL
- 动态发现类型
- Attribute(特性)
- 自定义特性
- 使用反射读取特性(这是反射最重要的应用之一)
总结
可以把反射理解为下面这张关系图:
程序集(Assembly)
│
▼
Type(类型)
│
┌──────┼─────────────┐
▼ ▼ ▼
属性 方法 构造函数
PropertyInfo MethodInfo ConstructorInfo
│ │ │
▼ ▼ ▼
读写属性 调用方法 创建对象
**掌握建议:**如果你正在系统学习 C#,建议先熟练掌握 委托、事件、泛型 ,再深入学习 反射 + 特性(Attribute)。这两者结合后,你就能理解很多现代 C# 框架的底层实现原理。