反射本质是读取程序集元数据而非反编译,用于运行时获取类型信息和动态调用,但性能开销大(如Invoke慢20–100倍),适用于插件、ORM、序列化等解耦场景,需避免滥用引发隐式依赖风险。

C# 反射不是“黑魔法”,而是对程序集元数据的读取和操作能力——它能让你在运行时知道一个类型长什么样、有哪些方法、怎么创建实例,但代价是明确的性能开销。
反射到底在干什么?本质是读元数据,不是反编译
你写的 C# 代码编译后会生成 .dll 或 .exe 文件,里面除了中间语言(MSIL),还有一块叫「元数据」的二进制区域。它像一张静态说明书,记录了所有类名、方法签名、属性、字段、构造函数、特性(Attribute)等信息。
反射(System.Reflection)干的事,就是按需打开这张说明书,查表、解析、调用——它不执行反编译,也不还原源码,只读元数据。
- ✅ 正确理解:反射 = 查类型说明书 + 按说明书动态干活
- ❌ 常见误解:“反射能绕过
private” —— 实际上只能访问private成员是因为BindingFlags.NonPublic允许你“翻说明书里的隐藏条目”,不是突破访问限制 - ⚠️ 注意:如果类型没被 JIT 编译过(比如从未实例化),它的元数据依然存在,反射照样能读到
Activator.CreateInstance 和 MethodInfo.Invoke 是性能重灾区
反射慢,主要慢在这两个环节:它们要走完整的“查找 → 绑定 → 安全检查 → 调用”链路,而直接 new 或方法调用是 JIT 编译后的一条机器指令。
- ✅
Activator.CreateInstance(typeof(MyClass))比new MyClass()慢 5–10 倍(无参构造);带参构造更慢,尤其参数类型匹配失败时还会抛异常 - ✅
methodInfo.Invoke(obj, args)比直接调用慢 20–100 倍,取决于参数数量和是否需要装箱/拆箱 - ✅ 性能敏感场景(如高频循环、实时服务)应避免在热路径中使用反射调用
- ? 替代方案:缓存
ConstructorInfo/MethodInfo实例;或用Expression.Lambda编译成委托(一次编译,多次调用接近原生速度)
什么时候必须用反射?别为了“炫技”硬上
反射不是通用工具,它是为特定解耦场景设计的——当编译时完全不知道类型,又必须在运行时跟它打交道时,才值得引入。
- ✅ 插件系统:主程序加载
Plugin.dll,通过约定接口名(如ICommand)查找并实例化实现类 - ✅ ORM 映射:
Dapper/EF Core用反射读取实体类的PropertyInfo,把数据库字段映射到对象属性 - ✅ 序列化框架:
Newtonsoft.Json用反射遍历属性生成 JSON 字符串 - ⚠️ 别滥用:比如只是想根据字符串名选一个已知的枚举值,用
Enum.Parse就够了,不需要Type.GetType(...).GetMethod(...).Invoke(...) - ⚠️ 隐藏风险:反射调用可能因类型版本变化、命名空间变更、强名称校验失败而突然崩溃,且错误堆栈难读
真正该警惕的,不是“反射慢”,而是“反射让调用关系脱离代码可见性”——你改了一个私有字段名,编译器不报错,但某处反射代码就默默挂了。这种隐式依赖,比性能更难维护。











