11

golang 用反射reflect操作结构体

 3 years ago
source link: https://blog.csdn.net/oqqYuan1234567890/article/details/90741112
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

golang 用反射reflect操作结构体

皿小草 2019-06-02 17:18:27 1634
分类专栏: golang
  • 需要遍历结构体的所有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的值也已经获取到


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK