Go反射调用方法前必须传入可寻址的指针,方法名须首字母大写,参数需严格匹配类型数量,调用前应校验IsValid、接收者及参数,返回值需手动Interface并断言。
Go 反射要求被调用方法的接收者是「可寻址」且「可设置」的。直接传 MyStruct{} 或函数返回的结构体字面量,reflect.ValueOf() 拿到的是不可寻址值,后续 Call() 必然 panic。
reflect.ValueOf(MyStruct{}).MethodByName("Do").Call(nil)
reflect.ValueOf(&M
yStruct{}).MethodByName("Do").Call(nil)
*MyStruct 类型(比如 obj := &MyStruct{}),就别再取地址:reflect.ValueOf(obj) 即可func (s MyStruct) Foo())也能被调用,但前提是传入的 reflect.Value 本身可寻址——仍建议统一传指针,避免行为不一致MethodByName() 遵循 Go 的导出规则:只有首字母大写的标识符才能跨包访问,反射也一样。非导出方法(如 bar()、doSomething())调用后返回零值 reflect.Value,IsValid() 为 false,直接 Call() 会 panic。
func (s *MyStruct) helper() {}
func (s *MyStruct) Helper() {}
if !method.IsValid() { return fmt.Errorf("method not found or unexported") }
[]reflect.Value,且类型、数量、顺序严格匹配签名Call() 不接受原始 Go 值,只认 []reflect.Value。每个参数都要用 reflect.ValueOf() 封装,且不能错位、不能少传、不能多传,连基础类型别名(如 type ID int)都不兼容。
func (c *Calculator) Add(a, b int) int
method.Call([]reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)})
reflect.ValueOf(int32(10)) 传给 int 参数 → panicmethod.Call([]reflect.Value{})(少参数)或 method.Call([]reflect.Value{a,b,c})(多参数)→ panicmethod.Type().NumIn() == len(args) 和 method.Type().In(i).AssignableTo(args[i].Type())
反射调用失败(方法不存在、参数错、接收者不可寻址等)全部以 panic 形式抛出。靠 defer/recover 捕获虽可行,但掩盖了本可静态发现的问题;更稳妥的是把校验做在调用前。
defer func(){ recover() }() 然后忽略错误IsValid() + Kind() == reflect.Func + 参数数量/类型校验 + 接收者非 nil[]reflect.Value,需手动 .Interface() 并断言,例如:results[0].Interface().(int);若不确定类型,先用 results[0].Kind() 判断Call() 仍可能触发目标方法内部 panic(比如除零、空指针解引用),这部分只能靠外层 recover 拦截,无法静态预防