PrintFields函数使用reflect包遍历并打印结构体导出字段名与值:先解引用指针,验证为结构体后,循环NumField()获取字段名和Interface()值。
在 Go 中实现通用打印函数来遍历任意对象的字段值,核心是使用 reflect 包。Go 没有泛型反射(直到 Go 1.18+ 泛型配合反射仍需手动处理结构体),但通过 reflect.Value 和 reflect.Type 可安全、递归地访问导出字段(首字母大写)的名称与值。
Go 的反射无法读取未导出字段(小写开头),这是语言设计的安全限制。以下函数仅遍历并打印结构体中可访问的字段名和值:
reflect.ValueOf(v).Kind() == reflect.Ptr 先解引用指针reflect.Struct),否则跳过或报错NumField(),用 Type.Field(i).Name 获取字段名,Value.Field(i).Interface() 获取值示例代码:
func PrintFields(v interface{}) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
fmt.Printf("n
ot a struct: %v\n", v)
return
}
typ := reflect.TypeOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
fmt.Println("Fields:")
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
value := val.Field(i).Interface()
fmt.Printf(" %s: %v\n", field.Name, value)
}
}
若想深入打印嵌套结构体、map、slice 等复合类型,需递归处理。注意控制深度防止无限循环(如循环引用),并区分基础类型与容器类型:
struct:递归调用自身,加缩进标识层级map:遍历 key-value,key 必须可比较(通常没问题),value 递归处理slice/array:遍历每个元素,递归打印interface{}:先取底层值再判断种类可封装为 PrintDeep(v interface{}, indent string),初始调用传 ""。
如果目标只是“可读地查看字段”,不必手写反射:
fmt.Printf("%+v\n", obj):标准库最简方式,显示字段名和值(含未导出字段为零值,不显示真实值)json.MarshalIndent(obj, "", " "):自动忽略未导出字段,输出格式化 JSON(要求字段可序列化)spew.Dump(obj),深度打印所有字段(含未导出字段,调试利器)反射易出错,实际使用需留意:
if v == nil 或 reflect.ValueOf(v).IsValid() 校验val.Elem() 再判断,否则 Kind() 可能是 Interface 而非目标类型