如何在Golang中获取方法信息_Golang reflect方法列表与调用示例

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,包含 NameTypeFunc 字段
  • 如果想获取接收者为指针的方法,要传入指针值: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 Valuecannot 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") → 编译报错,MethodByNamereflect.Value 方法,不是 Type

检查是否有效,永远用 if m.IsValid() { ... },不要跳过这步。

性能与替代方案:什么情况下不该用反射调方法

反射调用比直接调用慢 10–100 倍,且失去编译期类型检查。仅在真正需要动态分发时使用,比如插件系统、通用序列化框架、测试 mock 工具。

  • 若方法名固定,优先用接口抽象:type Speaker interface { Say() }
  • 若只是根据字符串选分支,用 map[string]func() 比反射更安全高效
  • 生成代码(如 go:generate + text/template)可完全避免运行时反射开销

最容易被忽略的是:反射无法绕过 Go 的可见性规则 —— 非导出方法永远拿不到,也调不了,这不是 bug,是设计使然。