看别人框架的源码,满屏的 Type、Assembly、GetMethod,一行 new 都没有,对象却一个个造出来了。这就是 c# 反射在起作用:程序跑起来之后,动态查看类型、创建实例、调用方法的机制。

反射能拿到什么

编译后的 dll 和 exe 里带着元数据,记录每个类型的字段、属性、方法、特性,System.Reflection 命名空间就是读取这堆元数据的入口。拿到一个 Type 对象,类型里有什么成员、各是什么访问级别,全都能查出来。这是普通代码做不到的——常规调用要求编译期就知道类型名,反射把这件事推迟到了运行时。

最常见的几步操作

  • 拿 Type:已知类型用 typeof(string),手里有对象就 obj.GetType(),按名字找用 Type.GetType,或者 Assembly.Load 加载程序集后再取
  • 造实例:Activator.CreateInstance(t) 返回 object,配合 ConstructorInfo 还能指定构造参数
  • 调方法:t.GetMethod(方法名) 拿到 MethodInfo,Invoke 传对象和参数就执行;私有成员要给 BindingFlags 参数
  • 读写属性:GetProperty 取 PropertyInfo,GetValue、SetValue 直接操作

反射都藏在哪些日常功能里

依赖注入容器按接口扫描实现类并自动注册,序列化库把对象属性逐个转成 JSON 字段,单元测试框架按特性找到测试方法自动执行,插件系统扫描目录里的 dll、筛选实现约定接口的类型再实例化——这些全靠反射撑着,只是被框架封装了,平时感觉不到。

性能和坑

反射调用比直接调用慢不少,还绕过了编译期检查:方法名写错,编译不报错,运行时才炸。优化思路是缓存,同一个 PropertyInfo、MethodInfo 反复用就存到字典里;高频路径还可以换委托或表达式树。另外启用程序集裁剪或发布 AOT 时,反射引用的类型可能被裁掉,得按框架要求做保留标注。

实际判断:写业务代码,编译期就确定的调用别绕弯用反射;做框架、插件、序列化这类需要动态扩展的场景,反射才是对的工具,配上缓存就够用。