

新闻资讯
技术学院应优先使用 reflect.TypeOf(x).Method(i) 遍历类型方法集;reflect.Value.Methods() 仅返回该值实例可调用的方法副本,且要求值非 nil、导出,而 Method.Func.Call() 需显式传入接收者。
reflect.Value.Methods() 获取可调用方法?直接调用 reflect.Value.Methods() 拿不到你想要的“方法列表”——它只返回值接收者的方法副本,且要求该值必须是导出的(首字母大写)、且不能是 nil 指针。更常见、更可靠的做法是用 reflect.TypeOf().Method(i) 遍历类型本身。
reflect.Value.Methods() 返回的是 []reflect.Method,但仅包含该具体值实例能调用的方法(比如指针值 vs 值类型值,方法集不同)reflect.TypeOf(x).NumMethod() + reflect.TypeOf(x).Method(i),它反映的是类型的完整方法集reflect.TypeOf(&x) 和 reflect.TypeOf(x) 的方法数可能不同:前者包含所有值接收者和指针接收者方法,后者只含值接收者方法reflect.Method.Func.Call() 会 panic: “call of reflect.Value.Call on zero Value”?这是最常踩的坑:拿到 reflect.Method.Func 后直接 .Call(),却忘了这个 Func 是一个未绑定接收者的函数值,它需要显式传入接收者作为第一个参数。
reflect.ValueOf(instance) 得到接收者值,再构造参数切片:append([]reflect.Value{receiver}, args...)
reflect.ValueOf(x) 即可;如果是指针接收者,必须传 reflect.ValueOf(&x),否则 Call() 会 panicreflect.Method.Func 类型是 reflect.Value,不是普通函数,不能直接调用反射不区分“标准库方法”和“用户定义方法”,但你可以通过包路径或方法签名做粗筛。实际项目中,更实用的是按命名约定或注释标记过滤,而非依赖包名——因为跨包导入后包路径可
能变。
m.PkgPath 判断是否为当前包方法:m.PkgPath == "" 表示导出方法(即首字母大写),但无法区分是否为标准库fmt.Stringer、json.Marshaler 等接口方法?得比对方法名和签名:m.Name == "String" && m.Type.NumIn() == 0 && m.Type.NumOut() == 1
Do、Handle、Process 开头的方法,避免误触 Reset 或 Close
type User struct {
Name string
}
func (u User) GetName() string { return u.Name }
func (u *User) SetName(n string) { u.Name = n }
func main() {
u := User{}
t := reflect.TypeOf(u)
v := reflect.ValueOf(u)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Printf("方法名: %s, 接收者类型: %s\n", m.Name, m.Type.In(0))
// 输出: GetName → 接收者是 User;SetName 不出现(因是 *User 接收者)
}
// 想看到 SetName,需用指针类型
tp := reflect.TypeOf(&u)
for i := 0; i < tp.NumMethod(); i++ {
m := tp.Method(i)
fmt.Printf("指针方法: %s\n", m.Name) // 输出 GetName 和 SetName
}
}
反射方法枚举本身不难,真正复杂的是接收者类型匹配、参数构造和错误传播控制——漏掉一个 & 或错传 reflect.Value 类型,就会在运行时崩,而且没有编译提示。