golang 理解 reflect包 函数 ValueOf, Value.Set

亡梦爱人 提交于 2019-12-05 01:03:09

最近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)
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!