Go 语言反射(Reflection)详解

反射是 Go 语言的高级特性,它允许程序在运行时检查变量的类型和值,以及动态操作这些变量。反射是 Go 语言中实现通用框架、序列化、依赖注入等功能的基础。


一、核心概念

Go 的反射主要通过 reflect 包实现,核心类型有:

  • reflect.Type:描述类型的信息
  • reflect.Value:描述值的信息
  • reflect.Kind:基本类型分类,如 struct、int、slice 等

反射可以做两件事:

  1. 获取类型和值的信息
  2. 动态修改值 (前提是值是可设置的 settable)

二、获取类型和值

示例:

go 复制代码
package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.14

	// 获取类型
	t := reflect.TypeOf(x)
	fmt.Println("Type:", t) // float64

	// 获取值
	v := reflect.ValueOf(x)
	fmt.Println("Value:", v) // 3.14

	// 获取 Kind
	fmt.Println("Kind:", v.Kind()) // float64
}

解释:

  • TypeOf 返回 reflect.Type,可以获取类型名、字段等信息
  • ValueOf 返回 reflect.Value,可以获取具体值
  • Kind 返回基本分类,用于判断类型,例如 reflect.Struct、reflect.Int 等

三、反射读取结构体字段

反射可以动态读取结构体字段的值和类型:

go 复制代码
type Person struct {
	Name string
	Age  int
}

func main() {
	p := Person{"Tom", 18}

	v := reflect.ValueOf(p)
	t := reflect.TypeOf(p)

	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		fieldType := t.Field(i)
		fmt.Printf("Field %s: %v = %v\n", fieldType.Name, fieldType.Type, field.Interface())
	}
}

输出:

复制代码
Field Name: string = Tom
Field Age: int = 18

说明:

  • NumField() 获取字段数量
  • Field(i) 获取值
  • t.Field(i) 获取类型和字段名
  • Interface() 可以将 reflect.Value 转回普通接口类型

四、修改结构体字段(必须是指针)

只有可设置的值才能修改:

go 复制代码
func main() {
	p := Person{"Tom", 18}

	v := reflect.ValueOf(&p).Elem() // 获取指针指向的值

	v.FieldByName("Name").SetString("Jerry")
	v.FieldByName("Age").SetInt(20)

	fmt.Println(p) // {Jerry 20}
}

注意:

  • ValueOf(&p) 必须传指针,否则修改会失败
  • Elem() 获取指针指向的值
  • 修改必须使用对应类型的 SetXXX 方法,例如 SetString、SetInt

五、动态调用方法

反射还可以动态调用方法:

go 复制代码
type Person struct {
	Name string
}

func (p Person) Greet(msg string) {
	fmt.Println(p.Name, "says:", msg)
}

func main() {
	p := Person{"Tom"}
	v := reflect.ValueOf(p)
	method := v.MethodByName("Greet")
	method.Call([]reflect.Value{reflect.ValueOf("Hello")})
}

输出:

复制代码
Tom says: Hello

说明:

  • MethodByName 获取方法
  • Call 动态调用,需要传 []reflect.Value 类型的参数

六、总结

  1. 反射可以在运行时获取变量的类型和值信息
  2. reflect.Type 获取类型信息,reflect.Value 获取值
  3. 修改值必须是可设置的,并且通常需要传入指针
  4. 可以通过反射动态访问字段和调用方法
  5. Go 的反射非常强大,但滥用会影响性能,通常用于框架、序列化、依赖注入等场景

七、最佳实践

  • 优先使用静态类型操作,反射作为最后手段
  • 修改结构体字段一定要传指针
  • 使用 Kind() 做类型判断,避免 panic
  • 反射代码复杂,调试时注意 Interface() 和 SetXXX 的类型匹配
相关推荐
码事漫谈6 小时前
骂AI,会让它做得更好吗?
后端
“AI国潮设计-小江”7 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
程序喵大人7 小时前
【C++入门】编译链接模型 - 01 一个 C++ 程序是怎样变成可执行文件的
开发语言·c++·编译链接模型
陈随易7 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
架构技术专栏8 小时前
AI Agent 框架怎么选:从一次制度查询拆出技术边界
后端·面试
sunshine22 girl8 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
菜鸟~noob2339 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
惊讶的猫10 小时前
工具选对之后还不够:商城 Agent 的参数与权限治理
开发语言·python
cmpxr_10 小时前
【MicroPython】如何刷新ESP8266的固件
开发语言·micropython
搭贝10 小时前
上厕所的时间搭好一套系统:AI自动生成实测
开发语言·人工智能·低代码·ai·php·搭贝