调用 reflect.Value.Call 前必须依次检查 Kind 是否为 reflect.Func、IsValid() 是否为 true、CanInterface() 是否为 true;方法调用需传地址,未导出函数或方法返回零值;参数须全为 []reflect.Value 并显式包装,空参用 nil;返回值需判空再解包;跨包调用需导出且手动注册函数映射。

reflect.Value.Call 调用前必须检查 Kind 和 Valid
直接 Call 一个未校验的 reflect.Value,十有八九会 panic。最常见的是 panic: call of reflect.Value.Call on zero Value——说明你传了个 nil 函数、未导出函数,或 MethodByName 没找到方法返回了零值。
- 先做
v.Kind() == reflect.Func判断,确保是函数类型(不是 struct、int 或 nil) - 再用
v.IsValid()确认值非空;v.CanInterface()确保能安全转回 interface{} - 对方法调用,
reflect.ValueOf(&obj).MethodByName("Foo")才有效;用reflect.ValueOf(obj)会导致 receiver 不可寻址,后续Call必 panic - 未导出函数(小写开头)或未导出方法,
MethodByName返回零值,IsValid()为 false
参数必须是 []reflect.Value,且每个元素都要包装
Call 只接受 []reflect.Value,不能传 []interface{} 或原始值。漏包、错包、类型不匹配,都会在运行时崩溃。
- 所有参数必须用
reflect.ValueOf(arg)显式包装,包括 int、string、struct 甚至 error - 如果函数签名是
func(*User, string) error,第一个参数就得是reflect.ValueOf(&user),不是reflect.ValueOf(user) - 空参调用写
method.Call(nil),别写method.Call([]reflect.Value{})(旧版本 Go 对空切片处理不一致) - 类型不兼容时(如传
int但期望int64),可用arg.Convert(targetType)强制转换,否则 panic
返回值要按索引和类型分别解包,error 不能忽略
Call 总是返回 []reflect.Value,哪怕函数没返回值(此时长度为 0)。直接取 results[0].Interface() 很危险——可能越界,也可能拿到 nil error。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 先判断
len(results) > 0,再用results[i].IsValid()防止空值 panic - 多返回值函数(如
(int, error)):用results[0].Int()取结果,results[1].Interface()取 error,再显式判空 - 不要对
results直接fmt.Println——它打印的是反射元信息,看不出真实值;要用.Interface()或对应取值方法(.Int()、.String()等) - 函数内部 panic 会透出到外层,
Call不捕获;如需兜底,只能在外层加defer/recover
字符串查函数名?Go 没这功能,得自己建 map
Go 编译后没有全局符号表,reflect 无法从字符串反查包级函数。所谓“通过名字调用”,本质是你自己维护一个注册表。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:定义
var funcMap = map[string]interface{}{"Add": Add, "Save": (*DB).Save},然后reflect.ValueOf(funcMap[name]) - 跨包调用时,目标函数必须导出(首字母大写),且注册时要传函数变量,不是字符串
- 别试图用
runtime.FuncForPC+ 名字匹配——不可靠、非标准、不同 Go 版本行为不一致 - 结构体方法要绑定 receiver:
funcMap["Save"] = reflect.ValueOf(&db).MethodByName("Save").Interface(),否则调用时 receiver 为 nil

















