反射仅适用于编译期类型不确定的场景,如插件加载;常见坑包括Type.GetType()因未指定程序集返回null、MethodInfo.Invoke()参数不匹配、私有成员需显式BindingFlags、泛型类型须MakeGenericType构造、高频调用应缓存反射对象。

反射不是万能胶,用错地方反而让代码更难维护、性能更差。它只在“编译期无法确定类型”时才该出现,比如插件加载、通用序列化、配置驱动的设备通信。
获取 Type 对象时,Type.GetType() 经常返回 null
这是最常踩的第一个坑:你以为传个字符串就能拿到类型,结果 Type.GetType("MyClass") 返回 null。根本原因是它默认只查当前程序集和 mscorlib,不跨 DLL。
- 要加载外部程序集里的类型,必须写全名 + 程序集标识,例如:
Type.GetType("MyNamespace.MyClass, MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") - 更稳妥的做法是先用
Assembly.LoadFrom("MyLibrary.dll")加载程序集,再调用assembly.GetType("MyNamespace.MyClass") - 如果类型名来自用户输入或配置文件,务必加空值检查:
if (type == null) throw new InvalidOperationException($"找不到类型: {typeName}");
调用方法时,MethodInfo.Invoke() 报错“参数数量不匹配”
常见于带 out 或 ref 参数的方法,或者重载方法没选对。反射不会自动帮你做参数推导,一切靠你手动对齐。
- 用
type.GetMethod("MethodName", new[] { typeof(string), typeof(int).MakeByRefType() })显式指定参数类型,尤其注意out/ref要用MakeByRefType() - 调用含
out参数的方法时,传入的object[]里对应位置必须是占位值(如0或null),返回后原数组会被修改,结果就写在那个位置上 - 静态方法调用时第一个参数必须是
null;实例方法则必须传真实实例,不能传null
读写私有成员必须显式传 BindingFlags
默认 GetProperty() 或 GetField() 只找 public 成员。想碰 private 字段或方法,不加 flag 就等于没写这行代码。
- 读写私有实例字段:
type.GetField("_value", BindingFlags.NonPublic | BindingFlags.Instance) - 调用私有实例方法:
type.GetMethod("DoWork", BindingFlags.NonPublic | BindingFlags.Instance) - 别漏掉
BindingFlags.Public或BindingFlags.Static—— 它们不是默认开启的,必须明确写出 - 多个 flag 用竖线
|连接,不是逗号;顺序无关,但少一个就查不到
泛型类型和方法反射容易漏掉构造步骤
拿到一个泛型类定义(比如 List<t></t>)的 Type,不能直接 Activator.CreateInstance() —— 它还没被“填上 T”。运行时必须先构造出具体类型。
- 判断是否为泛型定义:
type.IsGenericTypeDefinition为true表示它是List<t></t>这种模板,不是List<string></string> - 构造具体类型:
type.MakeGenericType(typeof(string))得到List<string></string>的Type - 泛型方法同理:
method.IsGenericMethod为true时,需先用method.MakeGenericMethod(typeof(int))得到可调用的MethodInfo
反射本身不慢,慢的是反复查 GetMethod()、GetProperty()。高频调用场景下,缓存 MethodInfo 或 PropertyInfo 实例比每次都查快一个数量级。另外,.NET 6+ 的 ReflectionContext 和源生成器已能替代部分反射需求——但前提是,你得先确认自己真需要它。


















