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