reflect 修改对象非导出字段的值

struct

但非导出的字段在外部也并不是没有办法访问, 也不是不可以修改的.

reflect 取地址访问和修改非导出字段

reflect.NewAt
// NewAt返回一个Value, 该指针表示一个指向指定类型, 使用p作为该指针.
func NewAt(typ Type, p unsafe.Pointer) Value {
	fl := flag(Ptr)
	t := typ.(*rtype)
	return Value{t.ptrTo(), p, fl}
}
NewAtPtrValueValueCanInterface()
Elem()CanSet()CanAddr()

注: NewAt 返回的"指针类型"的 reflect.Value. 指针的指向正是传入的地址 p.
要想获取"值(结构体)类型"的 reflect.Value, 只需要对返回值再执行 Elem() 操作即可.

UnsafeAddr()
// UnsafeAddr 返回指向 v 数据的指针.
// 适用于高级操作, 这些操作需要导入了 "unsafe" 包.
// 如果v不可寻址, 它会 panic
func (v Value) UnsafeAddr() uintptr {
	// TODO: deprecate
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.UnsafeAddr", Invalid})
	}
	if v.flag&flagAddr == 0 {
		panic("reflect.Value.UnsafeAddr of unaddressable value")
	}
	return uintptr(v.ptr)
}

注意: 调用者必须是指针类型.

Set
// Set 将 x 赋给值 v.
// 如果 CanSet 返回false, 则会 panic
// 和Go一样, x的值必须可分配给 v 的类型. (两者类型一致)
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
	}
}

注意: Set() 强调两件事, 一个调用者本身可以被设置, 另外一个设置的值和调用者本身一致.

结构体类型定义:

package t

type T struct {
    a string
}

访问非导出字段:

func GetPtrUnExportFiled(s interface{}, filed string) reflec.Value {
    v := reflect.ValueOf(s).Elem().FiledByName(filed)
    // 必须要调用 Elem()
    return reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()
}

修改非导出字段:

func SetPtrUnExportFiled(s interface{}, filed string, val interface{}) error {
     v := GetPtrUnExportFiled(s, filed)
     rv := reflect.ValueOf(val)
     if v.Kind() != v.Kind() {
        return fmt.Errorf("invalid kind, expected kind: %v, got kind:%v", v.Kind(), rv.Kind())
     }
     
     v.Set(rv)
     return nil
}

reflect 非取地址访问和修改非导出字段

New
// New返回一个Value, 该值表示指向指定类型的新零值的指针. 也就是说, 返回的值的类型为PtrTo(typ).
func New(typ Type) Value {
	if typ == nil {
		panic("reflect: New(nil)")
	}
	t := typ.(*rtype)
	ptr := unsafe_New(t)
	fl := flag(Ptr)
	return Value{t.ptrTo(), ptr, fl}
}
NewNewAtPtrValueNewAtNew

访问非导出字段:

func GetUnExportFiled(s interface{}, filed string) (accessableField, addressableSource reflec.Value) {
    v := reflect.ValueOf(s)
    
    // 创建一个指向新地址的 addressableSource, 由于这个原因, 无法修改 s 本身的字段的值
    addressableSource = reflect.New(v.Type()).Elem()
    addressableSource.Set(s)
    
    // 使用指针的方式一样
    accessableField = addressableSource.FiledByName(filed)
    accessableField = reflect.NewAt(accessableField.Type(), unsafe.Pointer(accessableField.UnsafeAddr())).Elem()
    
    return accessableField, addressableSource
}

设置非导出字段:

func SetUnExportFiled(s interface{}, filed string, val interface{}) error {
    v, addressableSource := GetUnExportFiled(s, filed)
    rv := reflect.ValueOf(val)
    if v.Kind() != v.Kind() {
        return fmt.Errorf("invalid kind, expected kind: %v, got kind:%v", v.Kind(), rv.Kind())
    }
         
    addressableSource.Set(rv)
    return nil
}