反射调用私有方法必须显式指定BindingFlags.NonPublic,否则GetMethod返回null;需组合BindingFlags.Instance或Static、注意大小写和重载;ref/out参数须用object数组中转;性能差且缺乏编译期检查,仅限必要场景使用。

反射调用私有方法时 BindingFlags 必须显式指定 BindingFlags.NonPublic
不加这个标记,GetMethod 或 GetMethods 默认只返回 public 成员,哪怕方法确实存在、签名完全匹配,也会返回 null。常见错误是只写 BindingFlags.Instance 或漏掉 BindingFlags.NonPublic,结果调用时报 NullReferenceException。
实际使用中,多数场景需要组合多个 flag:
-
BindingFlags.NonPublic—— 必须,否则看不到 private/protected 方法 -
BindingFlags.Instance—— 调用实例方法时必需;若调用 static 方法,则换用BindingFlags.Static -
BindingFlags.Public可同时带上,方便后续复用逻辑(比如统一获取所有可调用方法)
示例:
var method = obj.GetType().GetMethod("DoWork", BindingFlags.NonPublic | BindingFlags.Instance);
注意:方法名区分大小写,拼错或含重载时需进一步用 GetMethods() + 过滤参数类型来精确定位。
调用含 ref/out 参数的私有方法要传 object[] 且元素必须为引用类型包装
反射调用不支持直接传 ref/out 变量,必须把参数数组中的对应位置设为 object 类型变量,再通过 method.Invoke() 返回值反向提取修改后的值。常见坑是传原始值类型(如 int)进去,调用后原变量不变。
正确做法:
- 将 ref/out 参数声明为
object变量(如object arg1 = 42;) - 构造
object[] args = { arg1 }; - 调用
method.Invoke(obj, args) - 调用后从
args[0]读取更新后的值(已自动装箱/拆箱)
如果方法签名是 private void Swap(ref int a, ref int b),就不能直接传两个 int,而得用 object 中转。
性能差且绕过编译期检查,仅在必要时用反射调私有方法
反射调用比直接调用慢一个数量级以上,.NET 运行时无法内联、JIT 优化受限,还绕过了访问修饰符校验——这意味着单元测试能过,但后期重构删掉该私有方法时,编译器不会报错,运行时才崩。
更安全的替代路径:
- 把逻辑抽成
internal方法 +[InternalsVisibleTo]暴露给测试项目 - 用
dynamic(仅限 public 成员,不解决私有问题) - 改用源生成器(Source Generator)在编译期注入测试钩子
反射调私有方法本质是“破窗”,适合临时调试、适配遗留封闭库、或做 AOP 注入等基础设施层操作,不该出现在业务逻辑主干里。
泛型方法需先 MakeGenericMethod 才能调用
直接对泛型定义方法(如 private T GetValue)调用 GetMethod 返回的是开放泛型(GetValue`1),不能直接 Invoke。必须先用具体类型闭合它。
步骤明确:
- 用
GetMethod("GetValue")获取开放泛型MethodInfo - 调用
method.MakeGenericMethod(typeof(string))得到闭合版本 - 再用该闭合版
Invoke,否则抛InvalidOperationException: "Late bound operations cannot be performed..."
注意:闭合前无法获取参数类型、返回类型等元数据,调试时容易误判方法是否存在。
真正麻烦的不是怎么写那几行反射代码,而是维护成本——一旦被反射调用的私有方法签名变动,所有调用点全挂,还没法被 IDE 或编译器发现。










