FuncType.NumIn() 返回函数定义的形参个数,不包含接收者、不展开可变参数、与实参数量无关;调用 reflect.Value.Call() 时参数切片长度必须严格等于 NumIn(),否则 panic。

FuncType.NumIn 返回的是形参个数,不是实参个数
很多人调用 FuncType.NumIn() 后发现结果和自己传进去的参数数量对不上,其实它只看函数定义——哪怕你用 reflect.Call() 传了 5 个值,只要原函数签名是 func(int, string),NumIn() 就返回 2。
常见错误现象:NumIn() == 0 却以为函数没参数,其实是函数定义里真没写参数(比如 func());或者误把方法接收者当参数,但 NumIn() 不包含接收者。
- 方法转成
reflect.Value后,MethodByName得到的Value的Type()是带接收者的函数类型,但NumIn()仍只统计显式参数 - 闭包、函数变量、接口方法调用不影响
NumIn()结果,它只读取底层类型信息 - 如果函数有可变参数(
...T),NumIn()返回的是“展开后”的参数个数:例如func(...int)的NumIn()是1,不是不定长
获取参数类型要用 FuncType.In(i),别直接遍历 NumIn()
NumIn() 只给数量,真正要检查每个参数类型,得配合 FuncType.In(i) 用。下标从 0 开始,最大有效索引是 NumIn() - 1,越界会 panic。
使用场景:做参数校验、自动生成 API 文档、实现泛型约束模拟(Go 1.18 前)。
立即学习“go语言免费学习笔记(深入)”;
-
In(i)返回的是reflect.Type,不是值;要拿具体类型名用.Name()或.String() - 如果参数是未导出字段类型(比如
unexportedStruct),In(i).Name()返回空字符串,得用In(i).String() - 对于指针、切片、map 等复合类型,
In(i).Kind()才反映底层类别,Name()可能为空或不直观
NumIn 在反射调用前必须匹配,否则 Call panic
用 reflect.Value.Call() 时,传入的参数切片长度必须等于 NumIn(),少一个或多一个都会直接 panic:reflect: Call with too few args 或 reflect: Call with too many args。
性能影响:这个检查在运行时做,无编译期提示;兼容性上所有 Go 版本行为一致,但 Go 1.21+ 对 panic 信息更明确。
- 可变参数函数(
...T)传参时,要把可变部分展开成独立元素,例如fn.Call([]reflect.Value{a, b, c}),而不是包成一个 slice - 如果参数里含
nilinterface 或未初始化的reflect.Value,panic 信息可能误导,先用.IsValid()和.CanInterface()检查 - 方法调用要注意:绑定接收者后的
reflect.Value,其Type().NumIn()和原方法签名一致,不额外多算接收者
FuncType.NumIn 对比 FuncType.NumOut:别混淆输入输出
有人查完 NumIn() 接着想当然地用 NumOut() 当参数个数,结果出错。这两个完全独立:NumIn() 是输入参数个数,NumOut() 是返回值个数,函数可以 NumIn()==0 但 NumOut()==3(比如返回三个值)。
容易踩的坑:写通用 wrapper 时,只判断 NumIn() 却忘了函数可能有多个返回值,导致 Call() 后取 results[0] panic。
-
NumOut()为0表示无返回值,此时Call()返回空 slice,不能直接取[0] - 多返回值函数,
Call()返回的[]reflect.Value长度恒等于NumOut(),和NumIn()无关 - 如果函数返回
error,它的类型就是reflect.TypeOf((*error)(nil)).Elem(),不是特殊处理项
事情说清了就结束。注意 NumIn() 是静态的、只读的、不包含接收者也不展开可变参数——它只是函数签名里明明白白写着的那几个形参的数量。










