Go反射获取方法需用reflect.TypeOf(obj).NumMethod()和Method(i)遍历导出方法;指针接收者方法须传指针类型;调用前须用IsValid()检查,且参数需包装为[]reflect.Value;非导出方法不可见不可调。

怎么用 reflect.Method 获取结构体的方法列表
Go 的反射不能直接列出所有方法,必须先拿到结构体类型的 reflect.Type,再调用 NumMethod() 和 Method(i) 逐个提取。注意:只返回**导出方法**(首字母大写),非导出方法会被忽略。
-
reflect.ValueOf(obj).Type()或reflect.TypeOf(obj)获取类型对象 -
Method(i)返回的是reflect.Method,包含Name、Type、Func字段 - 如果想获取接收者为指针的方法,要传入指针值:
reflect.ValueOf(&obj).Type()
type User struct{}
func (u User) Say() { fmt.Println("hi") }
func (u *User) Walk() { fmt.Println("walk") }
t := reflect.TypeOf(User{})
fmt.Println(t.NumMethod()) // 输出 1(只有 Say)
t2 := reflect.TypeOf(&User{})
fmt.Println(t2.Elem().NumMethod()) // 仍为 1;但 t2.NumMethod() 是 2(Say + Walk)
如何通过 reflect.Value.Call() 调用方法
调用前必须确保:方法可导出、接收者类型匹配、参数数量和类型正确。最常见错误是传入值类型却调用指针接收者方法,或反之 —— 这会 panic 报错 call of reflect.Value.Call on zero Value 或 cannot call pointer method on ...。
- 用
reflect.ValueOf(obj).MethodByName("MethodName")获取可调用的reflect.Value - 参数需包装成
[]reflect.Value,每个元素用reflect.ValueOf(arg)构造 - 返回值也是
[]reflect.Value,需手动取[0].Interface()转回原类型
u := User{}
v := reflect.ValueOf(&u) // 必须传 &u 才能调 Walk
m := v.MethodByName("Walk")
if m.IsValid() {
m.Call(nil) // 无参数
}
// 调用有参数的方法
func (u *User) Greet(name string) string { return "Hello " + name }
g := v.MethodByName("Greet")
ret := g.Call([]reflect.Value{reflect.ValueOf("Alice")})
fmt.Println(ret[0].Interface()) // Hello Alice
为什么 reflect.Value.Method() 有时返回无效值
根本原因是反射对象未绑定到实际实例,或接收者类型不匹配。比如对 nil 指针调用、对非指针值调用指针接收者方法、或用 reflect.TypeOf()(只返回类型)误当 reflect.ValueOf()(才含值和可调用性)使用。
-
reflect.ValueOf(nil).MethodByName("X")→Invalid -
reflect.ValueOf(User{}).MethodByName("Walk")→Invalid(Walk 是 *User 接收者) -
reflect.TypeOf(User{}).MethodByName("Say")→ 编译报错,MethodByName是reflect.Value方法,不是Type的
检查是否有效,永远用 if m.IsValid() { ... },不要跳过这步。
立即学习“go语言免费学习笔记(深入)”;
性能与替代方案:什么情况下不该用反射调方法
反射调用比直接调用慢 10–100 倍,且失去编译期类型检查。仅在真正需要动态分发时使用,比如插件系统、通用序列化框架、测试 mock 工具。
- 若方法名固定,优先用接口抽象:
type Speaker interface { Say() } - 若只是根据字符串选分支,用 map[string]func() 比反射更安全高效
- 生成代码(如 go:generate + text/template)可完全避免运行时反射开销
最容易被忽略的是:反射无法绕过 Go 的可见性规则 —— 非导出方法永远拿不到,也调不了,这不是 bug,是设计使然。










