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() { ... },不要跳过这步。
反射调用比直接调用慢 10–100 倍,且失去编译期类型检查。仅在真正需要动态分发时使用,比如插件系统、通用序列化框架、测试 mock 工具。
type Speaker interface { Say() }
最容易被忽略的是:反射无法绕过 Go 的可见性规则 —— 非导出方法永远拿不到,也调不了,这不是 bug,是设计使然。