MethodInfo.Invoke传null仅对静态方法有效;调用实例方法必须传入具体对象实例,否则抛TargetException。

MethodInfo.Invoke 传 null 当第一个参数?只对静态方法有效
调用实例方法时传 null 给 Invoke 的第一个参数,运行就炸——抛 TargetException: Object reference not set to an instance of an object。这不是权限或路径问题,是语义错误:实例方法必须绑定到具体对象上。
- 静态方法才允许传
null,例如Math.Max、DateTime.Now这类 - 实例方法必须先拿到目标对象,比如
var obj = new Calculator(),再把obj当作第一个参数传进去 - 别指望
Activator.CreateInstance后直接丢掉引用——你得存住它,后面调用方法还得靠它 - 如果类型只有带参构造函数,
Activator.CreateInstance(type, args)要确保args类型和数量完全匹配,否则抛MissingMethodException
参数数组不是“随便塞值”,类型错一位就报 ArgumentException
Invoke 不做隐式转换,哪怕 int 到 long 这种数值兼容也不行。错误信息典型是:Object of type 'System.Int32' cannot be converted to type 'System.Int64'。
- 手动转类型:传
new object[] { (long)5, (long)3 },而不是new object[] { 5, 3 } - 对可空类型(如
string?或int?),null必须显式声明为对应类型,不能裸写null;建议用Convert.ChangeType(val, paramType)做兜底适配 - 数组长度必须严格等于方法签名参数个数,少一个、多一个都触发
TargetParameterCountException - 泛型方法要先调
MakeGenericMethod(typeof(int))得到封闭方法,再Invoke,否则找不到匹配项
ref/out 参数怎么传?别想让反射自动解包
反射不会帮你拆箱 ref int,也不会捕获 out string 的结果。你传进去的是什么容器,它就原样认什么——而 C# 的 ref 不能直接装箱。
- 对
void M(ref int x),得先声明变量:int x = 42;,再传new object[] { x } - 调用完,
Invoke返回的仍是object[],你要自己取回:x = (int)args[0];(注意:原始变量x本身不会变) -
out参数同理,初始值可以是任意值(甚至null),但数组位置和类型必须对,调用后同样要手动从返回数组里取 - 真要频繁处理
ref/out,不如用MethodInfo.CreateDelegate(.NET Core 2.1+ / .NET 5+),它支持强类型委托签名,安全又快
别在循环或高频路径里用 Invoke,慢且易崩
MethodInfo.Invoke 每次都要校验访问性、参数类型、装箱拆箱、执行上下文……实测比直接调用慢 50–100 倍以上,还容易因权限、加载上下文、泛型闭包等问题在运行时突然失败。
- 热路径(比如每帧、每次请求)务必缓存委托:
var del = method.CreateDelegate>(obj); del(5, 3); - 配置驱动的策略分发,优先用字典映射:
var handlers = new Dictionary,查表执行,零反射开销{ ["save"] = _svc.Save }; - 真要执行用户输入的表达式,用
Microsoft.CodeAnalysis.CSharp.Scripting,但记得加沙箱和超时控制,别图省事硬上Invoke
最常被忽略的一点:ref 和 out 的处理不是“多传个参数”那么简单,它要求你全程管理变量生命周期和值同步——这已经超出反射的舒适区了。










