Go 反射学习01-反射基础
什么是反射
反射(Reflection)是指在程序运行时能够检查自身结构的能力。
Go 语言通过 reflect 包提供了反射功能,允许程序在运行时检查变量的类型和值,甚至修改它们。
反射的主要用途包括:
- 编写灵活的代码,处理未知类型的值
- 实现通用的序列化/反序列化功能
- 构建 ORM 框架
- 实现依赖注入等高级特性
反射基础
.Type/.Value
Go 反射的核心是两个类型:
reflect.TypeOf:获取变量的类型,返回 reflect.Type
类型。
reflect.ValueOf:获取变量的值,返回 reflect.Value
类型。
reflect.Value
是一个结构体类型,通过 reflect.Value
可以获取到关于该变量的很多信息,比如类型、值、方法等。
go
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x)) // type: float64
fmt.Println("value:", reflect.ValueOf(x)) // value: 3.4
}
获取原始值
可以使用 Value 类型的 Interface()
方法获取原始值:
go
v := reflect.ValueOf(x)
y := v.Interface().(float64) // 类型断言
fmt.Println(y) // 3.4
反射的三大法则
- 从接口值到反射对象:
reflect.TypeOf
和reflect.ValueOf
将接口值转换为反射对象 - 从反射对象到接口值:
reflect.Value.Interface()
将反射对象转换回接口值 - 要修改反射对象,其值必须可设置:反射对象必须持有原始值的指针才能修改它
反射简单示例
检查类型和值
go
package main
import (
"fmt"
"reflect"
)
// inspectVariable 检查变量
func inspectVariable(v interface{}) {
t := reflect.TypeOf(v)
fmt.Println("Type:", t)
fmt.Println("Kind:", t.Kind())
val := reflect.ValueOf(v)
fmt.Println("Value:", val)
if t.Kind() == reflect.Int {
fmt.Println("It's an integer with value:", val.Int())
}
}
func main() {
inspectVariable(42)
inspectVariable("hello")
inspectVariable([]int{1, 2, 3})
}
运行的结果为:
go
// Type: int
// Kind: int
// Value: 42
// It's an integer with value: 42
// Type: string
// Kind: string
// Value: hello
// Type: []int
// Kind: slice
// Value: [1 2 3]
修改值
go
package main
import (
"fmt"
"reflect"
)
func main() {
x := 10
v := reflect.ValueOf(&x).Elem() // 获取可设置的Value
fmt.Println("Original value:", x) // Original value: 10
v.SetInt(20)
fmt.Println("Modified value:", x) // Modified value: 20
}
调用函数
go
package main
import (
"fmt"
"reflect"
)
func main() {
// 定义一个加法函数
add := func(a, b int) int {
return a + b
}
// 通过反射调用
v := reflect.ValueOf(add)
args := []reflect.Value{
reflect.ValueOf(10),
reflect.ValueOf(20),
}
result := v.Call(args)
fmt.Println("Result:", result[0].Int()) // 30
}
结构体反射
go
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{"Alice", 30}
v := reflect.ValueOf(p)
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s (%s) = %v\n", field.Name, field.Type, value.Interface())
}
}
运行的结果为:
go
// Name (string) = Alice
// Age (int) = 30
反射的注意事项
- 性能考虑:反射操作比直接代码调用慢,应避免在性能敏感的场景使用
- 类型安全:反射绕过了编译时类型检查,可能导致运行时错误
- 代码可读性:反射代码通常较难理解和维护
实际应用示例
遍历结构体
使用反射来遍历结构体的字段、调用结构体的方法、并获取结构体标签的值。
go
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print() {
fmt.Println("---- start ----")
fmt.Println(s)
fmt.Println("---- end ----")
}
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}) {
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
numField := val.NumField()
fmt.Printf("struct has %d fields\n", numField)
for i := 0; i < numField; i++ {
fmt.Printf("field %d: 值为=%v\n", i, val.Field(i))
tagVal := typ.Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("field %d: tag为=%v\n", i, tagVal)
}
}
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods \n", numOfMethod)
// 方法的排序是默认按照 函数名排序(ASCII码)
val.Method(1).Call(nil) // 获取到第二个方法 调用它
// 调用结构体第1个方法
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params)
fmt.Println("res=", res[0].Int()) // 返回结果 返回的结果是 []reflect.Value
}
func main() {
var a Monster = Monster{
Name: "黄鼠狼",
Age: 400,
Score: 30.1,
Sex: "男",
}
TestStruct(a)
}
运行的结果为:
go
// struct has 4 fields
// field 0: 值为=黄鼠狼
// field 0: tag为=name
// field 1: 值为=400
// field 1: tag为=monster_age
// field 2: 值为=30.1
// field 3: 值为=男
// struct has 3 methods
// ---- start ----
// {黄鼠狼 400 30.1 男}
// ---- end ----
// res= 50
遍历结构体 2
使用反射的方式来获取结构体的标签、遍历字段的值、修改字段值、调用结构体方法。
go
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print() {
fmt.Println("---- start ----")
fmt.Println(s)
fmt.Println("---- end ----")
}
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}) {
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct && kd != reflect.Ptr {
fmt.Println("expect struct")
return
}
numField := val.Elem().NumField()
fmt.Printf("struct has %d fields\n", numField)
// 修改 name 属性值
val.Elem().Field(0).SetString("嘿嘿嘿")
for i := 0; i < numField; i++ {
fmt.Printf("field %d: 值为=%v\n", i, val.Elem().Field(i))
tagVal := typ.Elem().Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("field %d: tag为=%v\n", i, tagVal)
}
}
numOfMethod := val.Elem().NumMethod()
fmt.Printf("struct has %d methods \n", numOfMethod)
// 方法的排序是默认按照 函数名排序(ASCII码)
val.Elem().Method(1).Call(nil) // 获取到第二个方法 调用它
// 调用结构体第1个方法
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Elem().Method(0).Call(params)
fmt.Println("res=", res[0].Int()) // 返回的结果是 []reflect.Value
}
func main() {
var a Monster = Monster{
Name: "黄鼠狼",
Age: 400,
Score: 30.1,
Sex: "男",
}
TestStruct(&a)
}
运行的结果为:
go
// struct has 4 fields
// field 0: 值为=嘿嘿嘿
// field 0: tag为=name
// field 1: 值为=400
// field 1: tag为=monster_age
// field 2: 值为=30.1
// field 3: 值为=男
// struct has 3 methods
// ---- start ----
// {嘿嘿嘿 400 30.1 男}
// ---- end ----
// res= 50
JSON 编码器
简化版的 JSON 编码器实现。
go
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
}
func ToJSON(v interface{}) (string, error) {
var result string
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Struct:
result += "{"
for i := 0; i < val.NumField(); i++ {
field := val.Type().Field(i)
fieldValue := val.Field(i)
if i > 0 {
result += ","
}
result += fmt.Sprintf("\"%s\":", field.Name)
switch fieldValue.Kind() {
case reflect.String:
result += fmt.Sprintf("\"%v\"", fieldValue.Interface())
case reflect.Int, reflect.Float64:
result += fmt.Sprintf("%v", fieldValue.Interface())
default:
subJson, _ := ToJSON(fieldValue.Interface())
result += subJson
}
}
result += "}"
case reflect.Slice, reflect.Array:
result += "["
for i := 0; i < val.Len(); i++ {
if i > 0 {
result += ","
}
subJson, _ := ToJSON(val.Index(i).Interface())
result += subJson
}
result += "]"
default:
return "", fmt.Errorf("unsupported type: %s", val.Kind())
}
return result, nil
}
func main() {
p := Person{"Bob", 25}
jsonStr, _ := ToJSON(p)
fmt.Println(jsonStr) // {"Name":"Bob","Age":25}
}
适配器函数
go
package main
import (
"fmt"
"reflect"
)
func main() {
call1 := func(v1, v2 int) {
fmt.Printf("v1 = %v v2=%v \n", v1, v2)
}
call2 := func(v1 int, v2 int, s string) {
fmt.Printf("v1 = %v v2=%v s=%v \n", v1, v2, s)
}
var (
function reflect.Value
inValue []reflect.Value
n int
)
bridge := func(call interface{}, args ...interface{}) {
n = len(args)
inValue = make([]reflect.Value, n)
for i := 0; i < n; i++ {
inValue[i] = reflect.ValueOf(args[i])
}
function = reflect.ValueOf(call)
function.Call(inValue)
}
bridge(call1, 1, 2) // v1 = 1 v2=2
bridge(call2, 1, 2, "hello") // v1 = 1 v2=2 s=hello
}
操作任意结构体
go
package main
import (
"fmt"
"reflect"
)
type user struct {
UserId string
Name string
}
func main() {
var (
model *user
sv reflect.Value
)
model = &user{}
sv = reflect.ValueOf(model)
sv = sv.Elem()
sv.FieldByName("UserId").SetString("6666")
sv.FieldByName("Name").SetString("wanglei-shuaige")
fmt.Println(*model) // {6666 wanglei-shuaige}
}
创建并操作结构体
go
package main
import (
"fmt"
"reflect"
)
type user struct {
UserId string
Name string
}
func main() {
var (
model *user
st reflect.Type
elem reflect.Value
)
st = reflect.TypeOf(model)
fmt.Println("reflect.TypeOf", st.Kind().String()) // reflect.TypeOf ptr
st = st.Elem()
fmt.Println("reflect.TypeOf.Elem", st.Kind().String()) // reflect.TypeOf.Elem struct
// New 返回一个 Value 类型值,该值持有一个指向类型为 type 的新申请的零值的指针
elem = reflect.New(st)
fmt.Println("reflect.New", elem.Kind().String()) // reflect.New ptr
fmt.Println("reflect.New.Elem", elem.Elem().Kind().String()) // reflect.New.Elem struct
// model 就是创建的 user 结构体的实例
model = elem.Interface().(*user) // model 是 *user 它的指向和 elem 是一样的
elem = elem.Elem() // 取得 elem 指向的值
elem.FieldByName("UserId").SetString("6666")
elem.FieldByName("Name").SetString("wanglei-shuaige")
fmt.Println(*model) // {6666 wanglei-shuaige}
}