
本文介绍如何利用go标准库reflect包,在运行时动态获取结构体的公开字段名和方法名,并说明导出规则及go惯用写法。
本文介绍如何利用go标准库reflect包,在运行时动态获取结构体的公开字段名和方法名,并说明导出规则及go惯用写法。
在Go中,若需在运行时探查结构体的内部结构(如字段名、方法名),必须借助reflect包——这是Go实现元编程能力的核心工具。需要注意:反射只能访问导出(即首字母大写)的字段和方法;小写开头的标识符属于未导出成员,即使存在,reflect也无法获取其信息,且对外不可见。
以下是一个完整示例,展示如何动态列出Student结构体的字段与方法:
package main
import (
"fmt"
"reflect"
)
type Student struct {
ID int // 导出字段:首字母大写
Name string // 导出字段
Age int // 导出字段
}
// 所有方法也需导出才能被反射识别
func (s *Student) SetID(i int) {
s.ID = i
}
func (s *Student) GetID() int {
return s.ID
}
func (s *Student) SetName(n string) {
s.Name = n
}
func (s *Student) GetName() string {
return s.Name
}
func (s *Student) SetAge(a int) {
s.Age = a
}
func (s *Student) GetAge() int {
return s.Age
}
func main() {
s := Student{ID: 1, Name: "Jack", Age: 22}
// 获取值类型(Student)的反射类型 → 用于遍历字段
t := reflect.TypeOf(s)
// 获取指针类型(*Student)的反射类型 → 用于遍历方法(因方法绑定在指针接收者上)
ptrT := reflect.TypeOf(&s)
// 提取导出字段名
var fields []string
for i := 0; i < t.NumField(); i++ {
fields = append(fields, t.Field(i).Name)
}
// 提取导出方法名
var methods []string
for i := 0; i < ptrT.NumMethod(); i++ {
methods = append(methods, ptrT.Method(i).Name)
}
fmt.Println("Fields:", fields) // [ID Name Age]
fmt.Println("Methods:", methods) // [GetAge GetID GetName SetAge SetID SetName]
}? 关键要点说明:
- 字段访问依赖值类型:reflect.TypeOf(s) 返回 Student 类型,可获取其导出字段;但若字段未导出(如 id int),将不会出现在结果中。
- 方法访问依赖接收者类型:本例中所有方法均定义在 *Student 上,因此需用 reflect.TypeOf(&s) 获取指针类型,再调用 NumMethod() 和 Method(i)。若方法定义在值接收者 Student 上,则应使用 reflect.TypeOf(s)。
- 导出规则是前提:Go中只有首字母大写的标识符(如 ID、GetName)才是导出的,才能被其他包访问,也才能被reflect检出。小写字段/方法(如 id、getName)在反射中完全不可见。
- Go惯用法提醒:不同于Java或C#,Go社区普遍避免冗余的 getter/setter。推荐直接暴露字段(如 s.Name = "Jack"),仅在需验证、封装逻辑或兼容接口时才定义方法。
✅ 总结:动态检视结构体需结合 reflect.TypeOf() 与正确的类型(值 or 指针)、严格遵守导出命名规范,并理解Go简洁直接的设计哲学——反射是利器,但应服务于清晰性与必要性,而非替代良好的API设计。
立即学习“go语言免费学习笔记(深入)”;


















