NumMethod() 返回结构体类型的方法数量:值类型只计接收者为自身的导出方法,指针类型额外包含接收者为指针的导出方法;遍历需用 Method(i) 获取名称与签名。

怎么用 reflect.TypeOf 获取结构体的方法数量
Go 语言中,reflect.TypeOf 返回的是接口类型的 reflect.Type,只有对指针或值类型调用才能拿到其方法集。注意:**非导出(小写开头)方法不会出现在 NumMethod() 计数里**,这是 Go 的反射规则,不是 bug。
正确做法是传入结构体的值或指针,再调用 NumMethod():
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
}
func (u User) GetName() string { return u.Name }
func (u *User) SetName(n string) { u.Name = n }
func (u User) privateMethod() {} // 不会被计入
func main() {
u := User{}
t := reflect.TypeOf(u)
fmt.Println(t.NumMethod()) // 输出:1(只算 GetName)
tPtr := reflect.TypeOf(&u)
fmt.Println(tPtr.NumMethod()) // 输出:2(GetName + SetName)
}
为什么 *T 和 T 的 NumMethod() 结果不同
Go 方法集定义明确:T 的方法集只包含接收者为 T 的方法;*T 的方法集包含接收者为 T 和 *T 的所有方法。反射忠实地复现了这一规则。
-
reflect.TypeOf(User{})→ 只能看到GetName -
reflect.TypeOf(&User{})→ 能看到GetName和SetName - 即使结构体字段不可寻址(如字面量、常量),只要类型是
*T,NumMethod()仍能返回完整方法数
如何遍历并打印所有方法名和签名
仅知道数量不够,调试或文档生成时通常需要具体方法信息。用 Method(i) 拿到 reflect.Method,它含 Name、Type、PkgPath(为空表示导出)等字段。
立即学习“go语言免费学习笔记(深入)”;
for i := 0; i < tPtr.NumMethod(); i++ {
m := tPtr.Method(i)
if m.PkgPath == "" { // 导出方法才处理
fmt.Printf("Method: %s, Type: %s\n", m.Name, m.Type.String())
}
}
注意:m.Type 是函数类型,比如 func(*main.User, string),不是方法签名字符串;若需参数名或返回值详情,得进一步用 m.Type.In(i) / m.Type.Out(i) 拆解。
常见误判点:嵌入字段方法是否被计入
结构体嵌入(embedding)不会自动把嵌入类型的方法“收编”进本类型的反射方法集中。也就是说:reflect.Type.NumMethod() **只统计直接定义在该类型上的方法**,不包含嵌入字段带来的方法。
例如:
type DB struct{}
func (db DB) Connect() {}
type App struct {
DB // 嵌入
}
func (a App) Run() {}
reflect.TypeOf(App{}).NumMethod() 返回 1(只有 Run),Connect 不在其中 —— 它属于 DB 类型,不是 App 的方法。
如果真要递归收集嵌入方法,得手动遍历字段、检查是否为匿名字段、再调用其 NumMethod(),但这种逻辑已超出标准反射能力范围,且容易混淆“谁的方法”。
实际项目中,别依赖反射自动聚合嵌入方法;该显式调用就显式调用,该封装接口就封装接口。










