Type.In 和 Type.Out 仅对 func 类型有效,需先用 t.Kind() == reflect.Func 判断,再调用 t.In(i) 或 t.Out(j),索引越界或非函数类型均会 panic。

Go 反射中 Type.In 和 Type.Out 的正确用法
这两个方法只对 func 类型有效,其他类型调用会 panic。别在 struct 或 int 上硬试 —— 它们不是函数,没入参也没出参。
实操前先确认类型:用 t.Kind() == reflect.Func 判断,再调 t.In(i) 或 t.Out(j)。否则 runtime error:reflect: Type.In of non-function type。
-
In(i)索引从 0 开始,超出范围(i >= t.NumIn())会 panic -
Out(i)同理,需满足i - 多返回值时,每个都算一个
Out,哪怕类型相同(如func() (int, int)→NumOut() == 2)
获取函数签名时绕不开的 reflect.TypeOf 预处理
必须传函数值(不是类型名、不是字符串),且不能是未初始化的 nil 函数变量。常见错误是传了 nil 或接口零值,结果 reflect.TypeOf(nil) 返回 nil,后续所有 In/Out 调用全崩。
正确姿势是确保有具体函数实例:
立即学习“go语言免费学习笔记(深入)”;
func add(a, b int) int { return a + b }
t := reflect.TypeOf(add) // ✅ 不是 reflect.TypeOf((*add)(nil)),也不是 reflect.TypeOf("add")
- 闭包函数也能用,但注意闭包捕获的变量不体现在参数类型里
- 方法值(如
obj.Method)和方法表达式(如T.Method)类型不同:前者是普通 func,后者是带 receiver 的 func,In(0)是 receiver 类型 - 如果函数定义在 interface 方法集中,得先取到具体实现再反射,不能对 interface 类型直接调
In
参数/返回值含命名类型时,In/Out 返回的是底层类型还是命名类型?
返回的是命名类型本身(如果存在),不是其底层类型。比如 type MyInt int,函数 func f(x MyInt) MyInt,那么 t.In(0) 和 t.Out(0) 都是 MyInt 类型的 reflect.Type,.Name() 是 "MyInt",.Kind() 才是 reflect.Int。
- 想判断是否为某个命名类型?用
t.Name() == "MyInt" && t.PkgPath() == "your/package",仅靠Name()不够(空包路径的 unnamed 类型返回空字符串) - 想比较底层行为一致?看
t.Kind()和t.Elem()(对 slice/map 等)或t.Field(i)(对 struct) - 别依赖
.String()做类型匹配,它包含包路径,跨包时容易误判
性能与泛用性:为什么生产代码里很少见 In/Out 直接裸用
反射本身有开销,而 In/Out 每次调用都涉及边界检查和内部索引查表。更关键的是:它们只告诉你“类型是什么”,不告诉你“这个参数叫什么名”或“是否有默认值”——Go 语言压根没有函数参数名反射支持。
- 写通用 HTTP handler 封装?别指望靠
In自动注入 context 或 request;老实用显式参数或中间件 - 做 ORM 参数绑定?优先走结构体 tag + 字段反射,函数签名反射在这里是歪路
- 真正需要它的场景其实很窄:比如 mock 框架生成桩函数、调试工具打印函数签名、或极简的 RPC 序列化桥接层
最常被忽略的一点:In 和 Out 对 variadic 函数(...T)返回的仍是单个 reflect.Type,不会自动展开成多个;NumIn() 对 func(int, ...string) 返回 2,最后一个 In(1) 是 []string 类型,不是 string。










