最近golang 项目需要一个通用函数,更新结构体中的数据。查看资料需要用到反射机制,之前对反射机制理解不深,费了些周折,终于有所理解,记录于此,供自己和大家参考。
理解interface{}变量
理解reflect 机制很重要一点,本人觉得关键需要理解把一个变量传给下面例子函数的interface{}参数后,interface{}获取了变量的哪些信息。
func reload(i interface{}) {
}通过reflect的unpackEface函数可以推测变量传给interface{}参数后,会把参数信息存储到emptyInterface结构体中
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don't read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}emptyInterface结构体包含变量的结构体信息和指向结构体的指针
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}理解reflect.ValueOf
reflect.ValueOf 实际是利用unpackEface函数把emptyInterface结构体中变量信息存于Value结构体
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// TODO: Maybe allow contents of a Value to live on the stack.
// For now we make the contents always escape to the heap. It
// makes life easier in a few places (see chanrecv/mapassign
// comment below).
escapes(i)
return unpackEface(i)
}理解reflect Value.Set
Value.Set 如下所示,把一个x中的数据赋值到v,但是赋值前需满足一些前提条件,其中一条是v必须是addressable,通过reflect.ValueOf 函数获取的Value 默认不是addressable(目前看代码是这样的)。可以通过Value.Elem(),获取addressable的Value,
func (v Value) Set(x Value) {
v.mustBeAssignable()
x.mustBeExported() // do not let unexported x leak
var target unsafe.Pointer
if v.kind() == Interface {
target = v.ptr
}
x = x.assignTo("reflect.Set", v.typ, target)
if x.flag&flagIndir != 0 {
typedmemmove(v.typ, v.ptr, x.ptr)
} else {
*(*unsafe.Pointer)(v.ptr) = x.ptr
}
}实际Value.Elem() 是给返回的新Value的flag 添加了flagAddr属性,当然Elem函数有个前提,调用Elem()的Value类型需要是指针(这里我们不涉及Value类型是interface的情况)func (v Value) Elem() Value {
k := v.kind()
switch k {
case Interface:
...
case Ptr:
ptr := v.ptr
if v.flag&flagIndir != 0 {
ptr = *(*unsafe.Pointer)(ptr)
}
// The returned value's address is v's value.
if ptr == nil {
return Value{}
}
tt := (*ptrType)(unsafe.Pointer(v.typ))
typ := tt.elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}最后附上利用反射机制实现更新结构体中数据的通用函数
package main
import (
"fmt"
"reflect"
)
type Index struct {
tm map[int32]string
}
func NewIndex() *Index {
index := &Index{}
index.tm = make(map[int32]string)
return index
}
func reload(i interface{}) {
r := NewIndex()
r.tm[2] = "nihao"
v := reflect.ValueOf(i)
fmt.Println("Type of v:", v.Type())
v.Elem().Set(reflect.ValueOf(*r))
}
func main() {
newIndex := NewIndex()
newIndex.AppIdTrans[1] = "Hello world"
reload(newIndex)
fmt.Println(newIndex)
}来源:CSDN
作者:逐风慕雨
链接:https://blog.csdn.net/youzhuceyici/article/details/79774204