Go 反射
1. 简介
Go语言的反射(reflection)通过reflect包提供,允许在运行时检查、修改对象的结构和行为。它主要用于动态类型处理、序列化/反序列化、依赖注入和测试等场景,但使用时需注意其性能开销、安全性问题和代码可读性。
核心方法
- reflect.TypeOf:返回变量的类型信息,静态反射,用于获取变量的类型描述。
- reflect.ValueOf:返回变量的值信息,动态反射,用于获取和操作变量的实际值。
2. 快速开始
2.1. 基础类型
var i int = 1
valueOf := reflect.ValueOf(i)
// 类型
fmt.Println(valueOf.Kind() == reflect.Int) // true
// 值
fmt.Println(valueOf.Interface()) // 1
// 设置值
valueOf = reflect.ValueOf(&i).Elem() // 因为要修改值,一定要传地址
valueOf.SetInt(99)
fmt.Println(i) // 99
2.2. 指针
var i int = 1
valueOf := reflect.ValueOf(&i)
// 检查类型
fmt.Println(valueOf.Kind() == reflect.Ptr) // true
// 取值
fmt.Println(valueOf.Elem().Interface()) // 1
fmt.Println(reflect.Indirect(valueOf).Interface()) // 1
2.3. 数组
var arr = [4]int{1, 2, 3, 4}
valueOf := reflect.ValueOf(&arr)
valueOf = reflect.Indirect(valueOf)
// 检查类型
fmt.Println(valueOf.Kind() == reflect.Array) // true
// 遍历
for i := 0; i < valueOf.Len(); i++ {
fmt.Println(valueOf.Index(i).Interface())
}
// 修改值
for i := 0; i < valueOf.Len(); i++ {
item := valueOf.Index(i)
valueOf.Index(i).SetInt(item.Int() * 2)
}
fmt.Println(arr) // [2 4 6 8]
2.4. 函数
func Sum(a, b int) int {
return a + b
}
valueOf := reflect.ValueOf(Sum)
methodType := valueOf.Type()
// 遍历参数类型
for i := 0; i < methodType.NumIn(); i++ {
paramType := methodType.In(i)
fmt.Println(paramType)
}
// 遍历返回值类型
for i := 0; i < methodType.NumOut(); i++ {
returnType := methodType.Out(i)
fmt.Println(returnType)
}
// 调用方法
results := valueOf.Call([]reflect.Value{
reflect.ValueOf(1),
reflect.ValueOf(2),
})
// 遍历返回值
for _, result := range results {
fmt.Println(result.Interface())
}
2.5. 结构体
2.5.1. 属性
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
person := Person{Name: "yimt", Age: 123}
pointerValueOf := reflect.ValueOf(&person)
valueOf := reflect.Indirect(pointerValueOf)
// 检查类型
fmt.Println(valueOf.Kind() == reflect.Struct) // true
// 值
fmt.Println(valueOf.Interface()) // {yimt 123}
// 遍历字段
structFields := reflect.VisibleFields(valueOf.Type())
for i := 0; i < valueOf.NumField(); i++ {
structField := structFields[i]
filed := valueOf.Field(i)
fmt.Printf("Name: %s, Tag: %s, Type: %s, Value: %v\n", structField.Name, structField.Tag.Get("json"), structField.Type, filed.Interface())
// Name: Name, Tag: name, Type: string, Value: yimt
// Name: Age, Tag: age, Type: int, Value: 123
}
// 修改值
for i := 0; i < valueOf.NumField(); i++ {
field := valueOf.Field(i)
// 这里简单按对应类型给值
if field.Kind() == reflect.String {
field.SetString("abc")
} else if field.Kind() == reflect.Int {
field.SetInt(100)
}
}
fmt.Println(person) // {abc 100}
2.5.2. 方法
type Person struct {
}
func (p *Person) Hello(name string) string {
return fmt.Sprintf("Hello %s", name)
}
person := new(Person)
valueOf := reflect.ValueOf(person)
person := new(Person)
valueOf := reflect.ValueOf(person)
typeOf := reflect.TypeOf(person)
// 遍历方法
for i := 0; i < valueOf.NumMethod(); i++ {
method := valueOf.Method(i)
typeMethod := typeOf.Method(i)
// 方法名
fmt.Println(typeMethod.Name)
// 调用方法
results := method.Call([]reflect.Value{
reflect.ValueOf("yimt"),
})
// 遍历返回参数
for j := 0; j < len(results); j++ {
fmt.Println(results[j].Interface()) // Hello yimt
}
}
// 按方法名查找
results := valueOf.MethodByName("Hello").Call([]reflect.Value{
reflect.ValueOf("yimt"),
})
for j := 0; j < len(results); j++ {
fmt.Println(results[j].Interface()) // Hello yimt
}
// 匹配指定参数和返回值方法
method := valueOf.MethodByName("Hello")
isMatch := method.Type() == reflect.TypeOf(func(str string) string {
panic("implement me")
})
fmt.Println(isMatch) // true
methodType := method.Type()
// 遍历方法输入参数
for i := 0; i < methodType.NumIn(); i++ {
paramType := methodType.In(i)
fmt.Println(paramType)
}
// 遍历方法返回参数
for i := 0; i < methodType.NumOut(); i++ {
returnType := methodType.Out(i)
fmt.Println(returnType)
}
2.6. 基础类型的slice
var s = []int{1, 2, 3, 4}
valueOf := reflect.ValueOf(&s)
valueOf = reflect.Indirect(valueOf)
// 检查类型
fmt.Println(valueOf.Kind() == reflect.Slice) // true
// 遍历
for i := 0; i < valueOf.Len(); i++ {
fmt.Println(valueOf.Index(i).Interface())
}
// 修改值
for i := 0; i < valueOf.Len(); i++ {
item := valueOf.Index(i)
item.SetInt(item.Int() * 2)
}
fmt.Println(s) // [2 4 6 8]
// 指向新的slice
newSlice := reflect.MakeSlice(valueOf.Type(), 0, 10)
for i := 0; i < 10; i++ {
newSlice = reflect.Append(newSlice, reflect.ValueOf(i+1))
}
valueOf.Set(newSlice)
fmt.Println(s) // [1 2 3 4 5 6 7 8 9 10]
2.7. 结构体类型的slice
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
var arr []Person = []Person{
{
Name: "yimt",
Age: 123,
},
}
r := reflect.ValueOf(&arr)
valueOf := reflect.Indirect(r)
// 类型
fmt.Println(valueOf.Kind() == reflect.Slice)
// 数据
// 遍历slice
for i := 0; i < valueOf.Len(); i++ {
item := valueOf.Index(i)
// 遍历结构体属性
structFields := reflect.VisibleFields(item.Type())
for j := 0; j < item.NumField(); j++ {
structField := structFields[j]
filed := item.Field(j)
fmt.Printf("Name: %s, Tag: %s, Type: %s, Value: %v\n", structField.Name, structField.Tag.Get("json"), structField.Type, filed.Interface())
}
}
// 修改slice
for i := 0; i < valueOf.Len(); i++ {
item := valueOf.Index(i)
// 遍历结构体属性
for j := 0; j < item.NumField(); j++ {
field := item.Field(j)
//
if field.Kind() == reflect.String {
field.SetString("abc")
} else if field.Kind() == reflect.Int {
field.SetInt(100)
}
}
}
fmt.Println(arr) // [{abc 100}]
// 修改slice指向
newSlice := reflect.MakeSlice(valueOf.Type(), 0, 1)
elemType := newSlice.Type().Elem()
obj := reflect.New(elemType).Elem()
obj.FieldByName("Name").SetString("haha")
obj.FieldByName("Age").SetInt(321)
newSlice = reflect.Append(newSlice, obj)
valueOf.Set(newSlice)
fmt.Println(arr) // [{haha 321}]
3. 实战
3.1. 对象反序列化
嵌套对象转成字节数据,这里只实现了uint32
和string
其他类型可以参考实现很简介。
- uint32转字节是小端4个字节。
- string因为是变长所以先有4个字节指定其长度,后面才是数据。
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"reflect"
"testing"
)
type MyBuffer struct {
*bytes.Buffer
}
func (b *MyBuffer) ReadBytes(length int) ([]byte, error) {
if b.Buffer.Len() < length {
return nil, errors.New("buffer is too small")
}
data := make([]byte, length)
_, err := b.Buffer.Read(data)
return data, err
}
func (b *MyBuffer) ReadUint32() (uint32, error) {
data, err := b.ReadBytes(4)
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(data), nil
}
func (b *MyBuffer) ReadString(length int) (string, error) {
data, err := b.ReadBytes(length)
if err != nil {
return "", err
}
return string(data), nil
}
type A struct {
A1 uint32
A2 uint32
B B
}
type B struct {
B1 string
}
func ObjectUnmarshal(data []byte, point interface{}) error {
mb := &MyBuffer{Buffer: bytes.NewBuffer(data)}
pointValue := reflect.ValueOf(point)
if pointValue.Kind() != reflect.Ptr {
return errors.New("point is not a pointer")
}
objValue := pointValue.Elem()
return reflectStruct(mb, objValue)
}
func reflectStruct(mb *MyBuffer, value reflect.Value) error {
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
switch field.Kind() {
case reflect.Struct:
if err := reflectStruct(mb, field); err != nil {
return err
}
case reflect.String:
length, err := mb.ReadUint32()
if err != nil {
return err
}
s, err := mb.ReadString(int(length))
if err != nil {
return err
}
field.SetString(s)
case reflect.Uint32:
u32, err := mb.ReadUint32()
if err != nil {
return err
}
field.Set(reflect.ValueOf(u32))
default:
return fmt.Errorf("%d no implment ", field.NumField())
}
}
return nil
}
func TestReflect(t *testing.T) {
data := []byte{
0x01, 0x00, 0x00, 0x00, // a.a1 uint32
0x02, 0x00, 0x00, 0x00, // a.a2 uint32
0x04, 0x00, 0x00, 0x00, // a.B uint32 字符串长度
}
data = append(data, []byte("abcd")...) // a.B 字符串数据
var a A
if err := ObjectUnmarshal(data, &a); err != nil {
t.Fatal(err)
}
t.Log(a)
}