methodinfo.invoke 用于通过反射调用方法或构造函数,需传入目标对象(静态方法可为null)和参数数组,返回方法结果或构造实例;参数类型、数量、顺序须严格匹配,ref/out需变量中转,私有成员需bindingflags显式指定。

MethodInfo.Invoke 不能直接当函数调用使,它是一把钝刀——能切,但得先磨刃、对准、压稳,否则不是切不断,就是伤手。
MethodInfo.Invoke 第一个参数传 null 就崩?看方法是不是 static
实例方法传 null 给 Invoke 的第一个参数,运行时必抛 TargetException: Object reference not set to an instance of an object。这不是权限问题,是语义错误:实例方法隐含 this,没对象就没上下文。
- 静态方法才允许传
null,例如Math.Max、DateTime.Now - 实例方法必须先拿到目标对象,比如
var obj = new Calculator(),再把obj当作第一个参数传进去 - 别指望
Activator.CreateInstance后不存引用——你得把它存下来,后面调用还靠它 - 如果用
typeof(IFoo).GetMethod("DoWork")拿到方法,却传入FooImpl实例,会报Object does not match target type;得确保传入对象的类型和声明该方法的Type严格一致(不能是子类或接口)
GetMethod 找不到私有方法?BindingFlags.NonPublic 必须显式写
GetMethod 默认只查 public 成员,哪怕方法真实存在、签名完全匹配,漏掉 BindingFlags.NonPublic 就返回 null,后续 Invoke 直接炸成 NullReferenceException。
- 调用私有实例方法:必须组合
BindingFlags.NonPublic | BindingFlags.Instance - 调用私有静态方法:用
BindingFlags.NonPublic | BindingFlags.Static - 方法名大小写敏感,拼错或存在重载时,
GetMethod可能返回null或错的方法;稳妥做法是先用GetMethods()拿全集,再按参数类型过滤 - 如果想统一处理公有/私有方法,可加
BindingFlags.Public一起传,避免逻辑分支
ref/out 参数传不进去?别裸传值,要用 object 变量中转
反射不支持直接传 ref int 或 out string,Invoke 不会自动解包,也不会捕获输出值。传原始值类型(如 int)进去,调用后原变量不变;传裸 null 给可空引用类型参数,也会因类型不匹配失败。
- ref 参数:先声明变量,如
object arg1 = 42;,再构造new object[] { arg1 },调用后从args[0]读更新值 - out 参数:初始值可为任意值(甚至
null),但数组位置和类型必须与签名完全对应,调用后同样要手动取回 - 数值类型参数不兼容(如
int传给long参数)会抛ArgumentException: Object of type 'System.Int32' cannot be converted to type 'System.Int64';需显式转换:new object[] { (long)5, (long)3 } - 数组长度必须严格等于方法参数个数,少一个或多一个都触发
TargetParameterCountException
泛型方法调用失败?MakeGenericMethod 是必过桥
泛型方法(如 T DoWork<t>()</t>)不能直接用 GetMethod("DoWork") 获取后调用,否则 Invoke 找不到匹配项,返回 null 或抛异常。
- 必须先用
method.MakeGenericMethod(typeof(int))构造封闭方法,再调Invoke - 若泛型参数是引用类型且可能为
null,注意typeof(string)和typeof(string?)在 .NET 5+ 是不同类型,需匹配准确 - 高频调用泛型方法时,
CreateDelegate不支持泛型签名,建议改用表达式树或DynamicMethod生成强类型委托 - 缓存委托比反复
Invoke快 50–100 倍以上;Delegate.CreateDelegate仅支持公开方法,私有方法需配合BindingFlags.NonPublic并确保权限
最易被忽略的一点:MethodInfo.Invoke 的每一次调用,都在运行时做访问性校验、参数类型比对、装箱拆箱、执行上下文切换——这些开销不会出现在编译期,也不会在单元测试里暴露。重构删掉一个私有方法,编译器不报错,上线后才崩。










