C# 反射调用方法方法 C#如何使用MethodInfo.Invoke动态调用方法

来源:这里教程网 时间:2026-02-21 17:40:25 作者:

MethodInfo.Invoke 调用实例方法必须传入目标对象

直接对实例方法调用

MethodInfo.Invoke(null, args)
会抛出
TargetException: Object reference not set to an instance of an object
。这是因为实例方法隐含一个
this
参数,反射必须知道调用在哪个对象上。

正确做法是先获取实例(比如 new 出来或从容器中取),再把该实例作为第一个参数传给

Invoke

var obj = new Calculator();
var method = typeof(Calculator).GetMethod("Add");
var result = method.Invoke(obj, new object[] { 5, 3 }); // 返回 8
静态方法才允许传
null
作第一个参数
若目标对象为
null
且方法非静态,异常发生在运行时,编译不报错
泛型方法需先用
MakeGenericMethod
构造封闭类型,再调用
Invoke

参数类型不匹配会导致 TargetParameterCountException 或 ArgumentException

Invoke
不自动做类型转换,哪怕数值上兼容(如传
int
long
参数)也会失败。错误信息通常是
System.ArgumentException: Object of type 'System.Int32' cannot be converted to type 'System.Int64'

解决方式是显式转换参数数组:

var args = new object[] { (long)5, (long)3 };
method.Invoke(obj, args);
使用
Convert.ChangeType(value, paramType)
可适配更多类型组合
注意值类型装箱后仍是原类型,不会“升级”成父类型 如果参数含
ref
out
,对应位置必须传
object
引用(如
new object[] { refVar }
),且调用后需手动解包

性能差、异常多,别在热路径里反复用 MethodInfo.Invoke

每次

Invoke
都要校验访问权限、参数个数、类型兼容性、执行上下文等,开销远高于直接调用。实测比直接调用慢 50–100 倍以上。

高频场景建议缓存委托:

var del = (Func<int, int, int>)Delegate.CreateDelegate(
    typeof(Func<int, int, int>), obj, method);
var result = del(5, 3); // 快,且类型安全
Delegate.CreateDelegate
仅支持公开实例/静态方法;私有方法需用
BindingFlags.NonPublic
并确保有权限
.NET 5+ 推荐用
DynamicMethod
或表达式树生成委托,更灵活
若只是偶尔调用(如插件加载、配置驱动逻辑),
Invoke
完全够用,不必过度优化

调用泛型方法前必须先构造具体类型

直接对开放泛型方法(如

List<t>.Add(T)</t>
)调用
Invoke
会失败,因为 CLR 不允许执行未闭合的泛型签名。

必须用

MakeGenericMethod
显式指定类型参数:

var list = new List<string>();
var method = typeof(List<string>).GetMethod("Add");
// 注意:这里 method 已是 closed 类型,可直接 Invoke
method.Invoke(list, new object[] { "hello" });
// 若只有开放泛型类型(如 typeof(List<>)),则:
var openMethod = typeof(List<>).GetMethod("Add");
var closedMethod = openMethod.MakeGenericMethod(typeof(string));
closedMethod.Invoke(list, new object[] { "world" });
MakeGenericMethod
返回新
MethodInfo
,原对象不变
泛型约束(如
where T : class
)会在
MakeGenericMethod
时检查,不满足则抛
ArgumentException
泛型类型参数不能是
var
或运行时才知的
Type
变量——必须在调用前确定
实际用起来最常卡在参数类型和对象实例这两处,尤其是从 JSON 或配置读参数后直接塞进
Invoke
,很容易因隐式类型丢失崩掉。

相关推荐