The sample
Sometimes obJ fields are not exported, although you can view the source code to know obJ data structure, but can not directly access the value of the specified field, with reflection can achieve the purpose
type A struct { num int64 level string b *B } type B struct { value string c *C } type C struct { name string id int64 } func getNestFile(i interface{}) { objA := reflect.ValueOf(i).Elem() objB := objA.Field(2).Elem() objC := objB.Field(1).Elem() idC := objC.Field(1).Int() fmt.Println(idC) } func main() { c := C{name: "namec", id: 100} b := B{value: "bvalue", c: &c} a := &A{num: 10, level: "high", b: &b} getNestFile(a) // 100 }Copy the code
Three laws of reflection
1. Reflection goes from interface value to reflection Object.
2. Reflection goes from refelction object to interface value.
3. To modify a reflection object, the value must be settable.
reflect.TypeOf
Returns the dynamic type of the parameter
type CustomizeInt int fmt.Println(reflect.TypeOf(1)) // int fmt.Println(reflect.TypeOf(1).Kind()) // int fmt.Println(reflect.TypeOf(CustomizeInt(1))) // main.CustomizeInt fmt.Println(reflect.TypeOf(CustomizeInt(1)).Kind()) // intCopy the code
reflect.ValueOf
The ValueOf source
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero 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)
}
Copy the code
ValueOf returns a new Value initialized with the Value of the argument I, which we can test:
a := A{Num: 100}
b := reflect.ValueOf(a).Interface().(A)
b.Num = 1
fmt.Println(a) // {100
}
fmt.Println(b) // {1
}
Copy the code
It’s still two unrelated values, so how can I change the field of object A? We usually think of Pointers, so here we’re trying to pass in the address of A, which is ampersand a
a := A{Num: 100}
fmt.Println(reflect.ValueOf(&a)) // &{100 <nil>}
fmt.Println(reflect.ValueOf(&a).Kind()) // ptr
reflect.ValueOf(&a).Field(0) // panic: reflect: call of reflect.Value.Field on ptr Value
Copy the code
ValueOf just returns a copy of the pointer, does not represent an object, so it cannot call the Field function
// Elem returns the value that the interface v contains
// or that the pointer v points to.
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
func (v Value) Elem(a) Value {
k := v.kind()
Copy the code
The pointer v points to returns an object to which a pointer points
a := A{Num: 100} fmt.Println(reflect.ValueOf(&a).Elem().Field(0).CanSet()) // true fmt.Println(reflect.ValueOf(&a).Elem().Type()) // min.ACopy the code
However, when changing the Num field to a non-exported Num, you cannot change the value in this way
fmt.Println(reflect.ValueOf(&a).Elem().Field(0).CanSet()) // false
Copy the code
You can use reflect.newat for the purpose of modifying non-exported fields
// NewAt returns a Value representing a pointer to a value of the
// specified type, using p as that pointer.
func NewAt(typ Type, p unsafe.Pointer) Value {
fl := flag(Ptr)
t := typ.(*rtype)
return Value{t.ptrTo(), p, fl}
}
Copy the code
func setUnExportValue(ptr interface{}, fileName string, newValue interface{}) error {
// Get the field to be modified
v := reflect.ValueOf(ptr).Elem().FieldByName(fileName)
v = reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()
nv := reflect.ValueOf(newValue)
ifv.Kind() ! = nv.Kind() {return fmt.Errorf("expected kind %v, got kind: %v", v.Kind(), nv.Kind())
}
v.Set(nv)
return nil
}
func main(a) {
a := A{num: 100}
setUnExportValue(&a, "num".int64(1))
fmt.Println(a) // {1
}
}
Copy the code