C# 反射详解

反射(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 倍(具体取决于运行环境和调用方式)。

因此:

不要在高频循环中频繁使用反射。

可以缓存 TypeMethodInfo,甚至结合委托来减少开销。


十六、反射的实际应用

几乎所有大型 C# 项目都会使用反射,例如:

场景 是否使用反射 作用
ASP.NET Core 自动发现控制器、Action
Entity Framework Core 映射实体与数据库
JSON 序列化(如 System.Text.Json) 读取对象属性进行序列化/反序列化
依赖注入(DI) 自动创建对象、注入依赖
插件系统 动态加载 DLL 并调用功能
Unity 查找带有特性的脚本、编辑器扩展

十七、反射学习路线

建议按以下顺序掌握:

  1. Type
    • 获取类型信息
    • 类型判断
  2. PropertyInfo
    • 获取属性
    • 读取与设置属性值
  3. MethodInfo
    • 查找并调用方法
    • 传递参数、处理返回值
  4. FieldInfo
    • 访问字段
  5. ConstructorInfo
    • 调用不同构造函数创建对象
  6. Assembly
    • 加载 DLL
    • 动态发现类型
  7. Attribute(特性)
    • 自定义特性
    • 使用反射读取特性(这是反射最重要的应用之一)

总结

可以把反射理解为下面这张关系图:

复制代码
程序集(Assembly)
        │
        ▼
     Type(类型)
        │
 ┌──────┼─────────────┐
 ▼      ▼             ▼
属性   方法        构造函数
PropertyInfo  MethodInfo  ConstructorInfo
 │          │            │
 ▼          ▼            ▼
读写属性    调用方法     创建对象

**掌握建议:**如果你正在系统学习 C#,建议先熟练掌握 委托、事件、泛型 ,再深入学习 反射 + 特性(Attribute)。这两者结合后,你就能理解很多现代 C# 框架的底层实现原理。

相关推荐
Cache技术分享1 小时前
465. Java 反射 - 获取类的简单名
前端·后端
做系统的大强1 小时前
REPL里的HLT:main_stmts为空时的致命尾声
后端
xlxxy_1 小时前
sap获取批次特性报表
java·linux·开发语言·前端·数据库·abap·mm
薛定谔的悦1 小时前
光伏-储能-负荷联合预测:给 EMS 装上"预知能力"
后端
云技纵横1 小时前
Redis 延迟双删还是读到脏数据?一次缓存一致性事故
redis·后端·面试
北冥you鱼1 小时前
Go语言常用包使用指南:从标准库到第三方利器
开发语言·microsoft·golang
网易云信1 小时前
从“合规”到“信任”:网易智企专属云打造强监管下的通信新范式
人工智能·后端
我才是银古1 小时前
一个人写出的 3D 几何引擎:Ara3D SDK 技术深度剖析
c#·三维几何·ai平台
俺不中嘞1 小时前
python常用函数
开发语言·python