背景
- 需要遍历结构体的所有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的值也已经获取到