golang 用反射reflect操作结构体

背景

  • 需要遍历结构体的所有field
  • 对于exported的field, 动态set这个field的value
  • 对于unexported的field, 通过强行取址的方法来获取该值(tricky?)

思路

下面的代码实现了从一个strct ptr对一个包外结构体进行取值的操作,这种场合在笔者需要用到反射的场合中出现比较多
simpleStrtuctField 函数接受一个结构体指针,因为最后希望改变其值,所以传参必须是指针。然后解引用。
接下来遍历结构体的每个field, exported字段是CanInterface的,对于unexported字段,需要强行取址来获取其值

model.go

package model

type Person struct {
	Name string
	age  int
}

func NewPerson(name string, age int) *Person {
	return &Person{
		Name: name,
		age:  age,
	}
}


main.go

package main

import (
	"github.com/miaomiao3/log"
	"../model"
	"reflect"
	"unsafe"
)

func main() {
	person := model.NewPerson("haha", 12)
	log.Debug("before:%+v", person)
	simpleStrtuctField(person)
	simpleStrtuctField(person)
	log.Debug("after:%+v", person)
}

// get unexported field
func simpleStrtuctField(v interface{}) {

	dataType := reflect.TypeOf(v)
	dataValue := reflect.ValueOf(v)

	if dataType.Kind() == reflect.Ptr {
		if dataValue.IsNil() {
			panic("nil ptr")
		}
		// 如果是指针,则要判断一下是否为struct
		originType := reflect.ValueOf(v).Elem().Type()
		if originType.Kind() != reflect.Struct {
			return
		}
		// 解引用
		dataValue = dataValue.Elem()
		dataType = dataType.Elem()
	} else {
		panic("non ptr")
	}

	num := dataType.NumField()
	for i := 0; i < num; i++ {

		field := dataType.Field(i)
		fieldName := field.Name

		fieldValue := dataValue.FieldByName(fieldName)
		if !fieldValue.IsValid() {
			continue
		}

		if fieldValue.CanInterface() {
			log.Debug("exported fieldName:%v value:%v", fieldName, fieldValue.Interface())

			if fieldValue.CanSet() && fieldValue.Kind() == reflect.String {
				oldValue := fieldValue.Interface().(string)
				fieldValue.SetString(oldValue + " auto append")
			}

		} else {
			// 强行取址
			forceValue := reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem()
			log.Debug("unexported fieldName:%v value:%v", fieldName, forceValue.Interface())
		}

	}

}

output:

2019/06/02 17:15:31.64 [D] before:&{Name:haha age:12}
2019/06/02 17:15:31.64 [D] exported fieldName:Name value:haha
2019/06/02 17:15:31.64 [D] unexported fieldName:age value:12
2019/06/02 17:15:31.64 [D] after:&{Name:haha auto append age:12}

可以看到,Name字段被反射改变了,age的值也已经获取到

你可能感兴趣的:(golang)