必须传函数变量(不带括号)给reflect.typeof,再通过t.in(i)和t.out(i)逐个获取参数与返回值类型;传myfunc()会得到返回值类型而非函数类型,且须先校验t.kind()==reflect.func并确保索引不越界。

怎么用 reflect.TypeOf 拿到函数的参数和返回值类型
直接传函数变量给 reflect.TypeOf,得到的是一个 reflect.Type 对象,它本身不暴露签名,必须调用 In(i) 和 Out(i) 才能逐个取类型。
常见错误是把 reflect.TypeOf(myFunc())(即调用结果)传进去,结果拿到的是返回值的类型,不是函数类型。必须传 myFunc 本身(不带括号)。
- 先校验
t.Kind() == reflect.Func,避免 panic -
t.NumIn()返回参数总数,t.In(i)取第 i 个参数类型(i 从 0 开始) -
t.NumOut()返回返回值个数,t.Out(i)取第 i 个返回值类型 - 若
t.NumOut() == 0,再调t.Out(0)会 panic,务必先判断
变参、接口、指针这些类型怎么识别
反射看到的类型是“原始签名”,比如 func(...int) 的最后一个参数,t.In(t.NumIn()-1) 返回的是 []int 类型,而不是“变参”这个概念本身。要识别变参,得靠 t.IsVariadic() —— 它只在最后一个参数是变参时返回 true。
接口类型(如 error、io.Reader)的 .Name() 通常为空,得用 .String() 才能看出具体名字;指针类型调 .Elem() 能拿到指向的类型,切片同理。
-
t.In(i).Kind() == reflect.Ptr表示是指针,再调.Elem()得到基类型 -
t.In(i).Kind() == reflect.Interface表示是接口,.Name()基本没用,优先看.String() - 变参必须结合
t.IsVariadic()判断,不能只看.Kind()
为什么反射调用函数时容易 panic
根本原因是 reflect.Value.Call 对参数类型、数量、顺序极其敏感,任何一项不匹配都会直接 panic,且错误信息不提示具体哪项错了。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
最常踩的坑是:把原始值(如 int)直接塞进参数切片,没用 reflect.ValueOf(x) 包装;或者该传指针却传了值,比如函数定义是 func(*string),却传了 reflect.ValueOf(s) 而不是 reflect.ValueOf(&s)。
- 所有参数必须是
[]reflect.Value,每个元素都得是reflect.ValueOf(x)构造的 - 如果函数参数是结构体字段,而字段未导出(小写开头),
reflect.ValueOf(struct).FieldByName("xxx")返回零值,后续 Call 必崩 - 未导出函数(小写开头)能获取类型,但
Call会报call of unexported method
命名返回值对反射有影响吗
没有。反射完全无视命名返回值的名字,只按顺序暴露类型列表。比如 func() (x int, err error) 和 func() (int, error) 在反射中完全一致:Out(0) 是 int,Out(1) 是 error。
这意味着你无法通过反射知道某个 error 返回值是不是叫 err,也无法知道哪个 int 对应 x。如果你依赖命名做逻辑判断(比如自动提取 err 返回值),得靠约定或额外注解,反射帮不上忙。
真正容易被忽略的是:哪怕命名返回值是导出类型(如 type Result struct{}),它的字段名是否导出,仍会影响反射能否读取其内部值 —— 这和函数签名本身无关,但常在后续处理返回值时暴露出来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










