Go 反射

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. 对象反序列化

嵌套对象转成字节数据,这里只实现了uint32string其他类型可以参考实现很简介。

  1. uint32转字节是小端4个字节。
  2. 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)
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yimtcode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值