go language foundation ------ 15 ------ reflection

Posted by nipsilanti on Thu, 03 Feb 2022 09:24:53 +0100

1 reflect

  • 1) Definition of reflection: in computer science, reflection refers to the access, detection and repair of computer programs at Run time
    An ability to change its own state or behavior. Figuratively speaking, reflection is that the program can "observe" and repair when running
    Change your behavior.

  • 2) The meaning of go reflection: the ORM Library of go language is inseparable from it, the json serialization Library of go language is inseparable from it, and fmt package word
    String formatting is inseparable from it, and the runtime of Go language is inseparable from it.

  • 3) Reflected target:

    1. Get the type information of the variable, such as the name of this type, the number of bytes occupied, all method lists, and all internal field knots
      Structure, its underlying storage type, and so on.
    2. Dynamically modify the internal field value of the variable. For example, in the deserialization of json, you have the names and corresponding values of the internal fields of the object,
      You need to loop the values of these fields into the corresponding fields of the object.
  • 4) Reflection in go language is realized through the reflection package, which realizes runtime reflection and allows programs to operate any Type of objects. The two key data classes in the reflect package are Type and Value.

func TypeOf(v interface{}) Type // The return type is actually an interface
func ValueOf(v interface{}) Value // Return value structure

For example:
var s int = 12
fmt.Println(reflect.TypeOf(s))  // output: int
fmt.Println(reflect.ValueOf(s)) // output: 12

2 reflect - pros and cons

1.1 benefits of reflection

  1. In order to reduce the bug rate caused by writing more code, do better reduction and abstraction.
  2. In order to be flexible, easy to use and convenient, do dynamic parsing, calling and processing.
  3. In order to make the code look good, easy to read and improve development efficiency, make up for some differences between dynamic language and dynamic language.

1.2 disadvantages of reflection

  1. Reflection related code is often difficult to read. In software engineering, code readability is also a very important index.
  2. Go language is a static language. In the coding process, the compiler can find some type errors in advance, but it is powerless to reflect code
    Force. Therefore, the code related to reflection may run for a long time before making an error. At this time, it is often a direct panic, which may cause
    Serious consequences.
  3. Reflection still has a great impact on performance, one to two orders of magnitude slower than normal code. Therefore, for a project in operation
    For code in key positions of line efficiency, try to avoid using reflection characteristics.

3. Reflect Type

3.1 Type

  • 1) Type: type is used to represent a go type.
  • 2) Not all types of go methods can use values. See the documentation for each method for usage restrictions. When calling a method with classification restrictions, you should first use the Kind method to know the classification of the Type. Calling a method that is not supported by this classification will result in panic at run time.
  • 3) Method to get Type object: func TypeOf(i interface{}) Type.
    For example:
str := "aaa"
res_type := reflect.TypeOf(str)
fmt.Println(res_type) //string

int1 := 1
res_type2 := reflect.TypeOf(int1)
fmt.Println(res_type2) //int

3.2 reflect.Type general method

func (t *rtype) String() string // Get the String description of t type. Do not judge whether the two types are consistent through String.
func (t *rtype) Name() string // Gets the name of the t type defined in its package, and returns an empty string for an unnamed type.
func (t *rtype) PkgPath() string // Gets the name of the package where the t type is located, and returns an empty string for unnamed types.
func (t *rtype) Kind() reflect.Kind // Gets the category of type t.
func (t *rtype) Size() uintptr // Get the size, function and unsafe of the value of type t when allocating memory Sizeof.
func (t *rtype) Align() int // Gets the byte alignment value of a value of type t when allocating memory.
func (t *rtype) FieldAlign() int // Gets the value of type t as the byte alignment value when the structure field is used.
func (t *rtype) NumMethod() int // Gets the number of methods of type t.
func (t *rtype) Method() reflect.Method // Get the method of type t according to the index. If the method does not exist, then panic.
// If t is an actual Type, the receiver will be listed in the Type and Func fields of the return value.
// If t is only an interface, the Type of the returned value does not list the receiver, and Func is null.
func (t *rtype) MethodByName(string) (reflect.Method, bool) // Gets the method of type t by name.
func (t *rtype) Implements(u reflect.Type) bool // Judge whether the t type implements the u interface.
func (t *rtype) ConvertibleTo(u reflect.Type) bool // Judge whether the value of type t can be converted to type u.
func (t *rtype) AssignableTo(u reflect.Type) bool // Judge whether the value of type t can be assigned to type u.
func (t *rtype) Comparable() bool // Judge whether the value of t type can be compared
//Note: for arrays, slices, maps, channels, pointers, interfaces
func (t *rtype) Elem() reflect.Type // Get the element type, get the object type indicated by the pointer, and get the dynamic type of the interface

For example:

package main

import (
	"fmt"
	"reflect"
)

type Skills interface {
	reading()
	running()
}

type Student struct {
	Age  int
	Name string
}

func (self Student) runing() {
	fmt.Printf("%s is running\n", self.Name)
}
func (self Student) reading() {
	fmt.Printf("%s is reading\n", self.Name)
}

func main() {
	stu1 := Student{Name: "aaa", Age: 34}
	inf := new(Skills)
	stu_type := reflect.TypeOf(stu1)

	fmt.Println("type stu_type:", stu_type) //main.Student
	fmt.Println(stu_type.String())       //main.Student
	fmt.Println(stu_type.Name())         //Student
	fmt.Println(stu_type.PkgPath())      //main
	fmt.Println(stu_type.Kind())         //struct
	fmt.Println(stu_type.Size())         //24

	fmt.Println("===================")

	inf_type := reflect.TypeOf(inf).Elem()                          //  Gets the object type indicated by the pointer
	fmt.Println("\n type inf_type:", inf_type)                          //main.Skills
	fmt.Println(inf_type.NumMethod())                               //2
	fmt.Println(inf_type.Method(0), "and", inf_type.Method(0).Name) // {reading main func() <invalid Value> 0} adn reading
	fmt.Println(inf_type.MethodByName("reading"))                   //{reading main func() <invalid Value> 0} true

}

3.3 reflect.Type other methods

// numerical value
func (t *rtype) Bits() int // Gets the bit width of a numeric type. t must be integer, floating point, or complex
// array
func (t *rtype) Len() int // Gets the number of elements of the array
// mapping
func (t *rtype) Key() reflect.Type // Gets the mapped key type
// passageway
func (t *rtype) ChanDir() reflect.ChanDir // Gets the direction of the channel
// structural morphology
func (t *rtype) NumField() int // Get the number of fields
func (t *rtype) Field(int) reflect.StructField // Get field by index
func (t *rtype) FieldByName(string) (reflect.StructField, bool) // Get field by name
func (t *rtype) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) // Gets the field according to the specified match function math
func (t *rtype) FieldByIndex(index []int) reflect.StructField // Get nested fields from index chain
// function
func (t *rtype) NumIn() int // Gets the number of arguments to the function
func (t *rtype) In(int) reflect.Type // Get the parameter information of the function according to the index
func (t *rtype) NumOut() int // Gets the number of return values of the function
func (t *rtype) Out(int) reflect.Type // Get the return value information of the function according to the index
func (t *rtype) IsVariadic() bool // Judge whether the function has variable parameters.

For example:

package main

import (
	"fmt"
	"reflect"
)

type Skills interface {
	reading()
	running()
}

type Student struct {
	Name string "json:name"
	Age  int    "json:age"
}

func (self Student) runing() {
	fmt.Printf("%s is running\n", self.Name)
}
func (self Student) reading() {
	fmt.Printf("%s is reading\n", self.Name)
}
func main() {
	stu1 := Student{Name: "aaa", Age: 34}
	stu_type := reflect.TypeOf(stu1)
	fmt.Println(stu_type.NumField())          //2
	fmt.Println(stu_type.Field(0))            //{Name  string  0 [0] false}
	fmt.Println(stu_type.FieldByName("Name")) //{{Age  int  16 [1] false} true
	fmt.Println(stu_type.Field(1))            //{Name  string  0 [0] false}
	fmt.Println(stu_type.FieldByName("Age"))  //{{Age  int  16 [1] false} true
}

3.3 Type structure
rtype implements all methods of the Type interface. The rest of the information is different from various special types of structures. rtype can be understood as a parent class. The structure of a special Type is a subclass, and there will be some different field information.

Source code path: C: \ go \ SRC \ reflect \ type go

// The basic Type rtype implements the Type interface
type rtype struct {
size uintptr // Occupied bytes
ptrdata uintptr
hash uint32 // hash value of type
...
kind uint8 // Meta type
...
}
// Slice type
type sliceType struct {
rtype
elem *rtype // Element type
}
// Structure type
type structType struct {
rtype
pkgPath name // Package name
fields []structField // Field list
}

4 reflect.Value method

  • 1)reflect.Value.Kind(): get variable category and return constant. Possible values of constants are as follows:
const (
Invalid Kind = iota //Type that does not exist
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr // Integer type of pointer
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)

For example:

str := "aaa"
val := reflect.ValueOf(str).Kind()
fmt.Println(val) //string
  • 2) Method to get value:
func (v Value) Int() int64 // Gets the value of int type. If the v value is not a signed integer, then panic.
func (v Value) Uint() uint64 // Get the value of unit type. If the v value is not an unsigned integer (including uintptr), then panic.
func (v Value) Float() float64 // Get the value of float type. If the v value is not float type, then panic.
func (v Value) Complex() complex128 // Get the value of complex type. If the v value is not complex, panic.
func (v Value) Bool() bool // Gets the value of boolean type. If the v value is not Boolean, then panic.
func (v Value) Len() int // Get the length of the v value. The v value must be string, array, slice, map and channel.
func (v Value) Cap() int // Get the capacity of v value. v value must be value, slice and channel.
func (v Value) Index(i int) reflect.Value // Get the ith element of v value. v value must be string, array and slice. i cannot exceed the range.
func (v Value) Bytes() []byte // Get the value of byte type. If the v value is not a byte slice, then panic.
func (v Value) Slice(i, j int) reflect.Value // Get the slice of V value, slice length = j - i, slice capacity = v.Cap() - i.
// v must be a string, numeric value, slice, and if it is an array, it must be addressable. i cannot exceed the range.
func (v Value) Slice3(i, j, k int) reflect.Value // Get the slice of v value, slice length = j - i, slice capacity = k - i.
// i. J and K cannot exceed the capacity of v. i <= j <= k. 
// v must be a string, numeric value, slice, and if it is an array, it must be addressable. i cannot exceed the range.
func (v Value) MapIndex(key Value) reflect.Value // Get the content of the v value according to the key. The v value must be a mapping.
// If the specified element does not exist or the v value is an uninitialized mapping, a zero value (reflect.ValueOf(nil)) is returned
func (v Value) MapKeys() []reflect.Value // Gets an unordered list of all keys for the v value, which must be a mapping.
// If the v value is an uninitialized mapping, an empty list is returned.
func (v Value) OverflowInt(x int64) bool // Judge whether x exceeds the value range of v value. v value must be signed integer.
func (v Value) OverflowUint(x uint64) bool // Judge whether x exceeds the value range of v value. v value must be unsigned integer.
func (v Value) OverflowFloat(x float64) bool // Judge whether x exceeds the value range of v value. v value must be floating-point type.
func (v Value) OverflowComplex(x complex128) bool // Judge whether x exceeds the value range of v value. v value must be complex.
  • 3) How to set the value:
func (v Value) SetInt(x int64) //Sets the value of type int
func (v Value) SetUint(x uint64) // Sets the value of an unsigned integer
func (v Value) SetFloat(x float64) // Sets the value of the floating point type
func (v Value) SetComplex(x complex128) //Set value of complex type
func (v Value) SetBool(x bool) //Sets the value of the boolean type
func (v Value) SetString(x string) //Sets the value of the string type
func (v Value) SetLen(n int) // Set the length of the slice. n cannot exceed the range and cannot be negative.
func (v Value) SetCap(n int) //Sets the capacity of the slice
func (v Value) SetBytes(x []byte) //Sets the value of the byte type
func (v Value) SetMapIndex(key, val reflect.Value) //The key and value of the map must be set on the premise that after initialization, there is overwrite and no addition

For example:

// Gets and sets a value of a common type
package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 1. Get value
	str := "aaa"
	age := 11
	fmt.Println(reflect.ValueOf(str).String()) //Get the value of str and the result is aaa
	fmt.Println(reflect.ValueOf(age).Int())    //Get the value of age, result 11
	str2 := reflect.ValueOf(&str)              //Get Value type

	// 2. Set value
	str2.Elem().SetString("bbb")  //Set value
	fmt.Println(str2.Elem(), age) //bbb 11

	// 3. Get the address type variable of its Value through reflection, and modify the variable, and the original variable age will change accordingly
	age2 := reflect.ValueOf(&age) //Get Value type
	fmt.Println("age2:", age2)    //0xc000012090

	age2.Elem().SetInt(40) //Set value
	fmt.Println("age:", age)
	fmt.Println("reflect.ValueOf(age):", reflect.ValueOf(age))
}

  • 4) Other methods:
//Structure related:
func (v Value) NumField() int // Get the number of structure fields (members)
func (v Value) Field(i int) reflect.Value //Get structure field based on index
func (v Value) FieldByIndex(index []int) reflect.Value // Get structure nested fields according to index chain
func (v Value) FieldByName(string) reflect.Value // Get the field of the structure according to the name. If it does not exist, return reflect ValueOf(nil)
func (v Value) FieldByNameFunc(match func(string) bool) Value // Get the field according to the matching function match. If there is no matching field, return
 Zero value( reflect.ValueOf(nil))
//Channel related:
func (v Value) Send(x reflect.Value)// When sending data (blocking), the v value must be a writable channel.
func (v Value) Recv() (x reflect.Value, ok bool) // When receiving data (blocking), the v value must be a readable channel.
func (v Value) TrySend(x reflect.Value) bool // When attempting to send data (without blocking), the v value must be a writable channel.
func (v Value) TryRecv() (x reflect.Value, ok bool) // When attempting to receive data (without blocking), the v value must be a readable channel.
func (v Value) Close() // Close channel
//Function correlation
func (v Value) Call(in []Value) (r []Value) // Call the function (or method) represented by the v value through the parameter list in. The return value of the function is stored in r.
// As many parameters as you want to pass in, you can store as many elements in in.
// Call can call fixed parameter functions (with a fixed number of parameters) or variable parameter functions (with a variable number of parameters).
func (v Value) CallSlice(in []Value) []Value	//Call variable parameter function

Example 1:

package main

import (
	"fmt"
	"reflect"
)

type Skills interface {
	reading()
	running()
}

type Student struct {
	Name string "json:name"
	Age  int    "json:age"
}

func (self Student) runing() {
	fmt.Printf("%s is running\n", self.Name)
}
func (self Student) reading() {
	fmt.Printf("%s is reading\n", self.Name)
}
func main() {
	stu1 := Student{Name: "aaa", Age: 34}
	stu_type := reflect.TypeOf(stu1)
	fmt.Println(stu_type.NumField())          //2
	fmt.Println(stu_type.Field(0))            //{Name  string  0 [0] false}
	fmt.Println(stu_type.FieldByName("Name")) //{{Age  int  16 [1] false} true
	fmt.Println(stu_type.Field(1))            //{Name  string  0 [0] false}
	fmt.Println(stu_type.FieldByName("Age"))  //{{Age  int  16 [1] false} true
}

result:

Example 2:

//Call the method in the structure through reflection, and use reflect Value. Method(i int). Call()
//Or reflect Value. MethodByName(name string). Call() implementation
package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name string
	Age  int
}

func (this *Student) SetName(name string) {
	this.Name = name
	fmt.Printf("set name %s\n", this.Name)
}

func (this *Student) SetAge(age int) {
	this.Age = age
	fmt.Printf("set age %d\n", age)
}

func (this *Student) String() string {
	fmt.Printf("this is %s\n", this.Name)
	return this.Name
}

func (this *Student) SetAgeAndName(age int, name string) {
	this.Age = age
	fmt.Printf("set age %d, name:%s\n", age, name)
}

func main() {
	// 1. Call method by name
	stu1 := &Student{Name: "aaa", Age: 18}
	val := reflect.ValueOf(stu1)         //Get the Value type, or use reflect ValueOf(&stu1). Elem()
	val.MethodByName("String").Call(nil) //Call String method

	params := make([]reflect.Value, 1)
	params[0] = reflect.ValueOf(18)
	val.MethodByName("SetAge").Call(params) //Call method by name

	params = make([]reflect.Value, 2)
	params[0] = reflect.ValueOf(18)
	params[1] = reflect.ValueOf("ccc")
	val.MethodByName("SetAgeAndName").Call(params)

	params = make([]reflect.Value, 1) // Reset the number of parameters to 1 first, otherwise an error will be reported below.

	// 2. Call the method by index. But it may not be safe.
	params[0] = reflect.ValueOf("bbb")
	// val.Method(1).Call(params) / / call through method index
	val.Method(2).Call(params) //Call through method index 	 It is not safe to get the function through the index, because the index corresponding to each method may be different, and an error may be reported if the parameter is wrong,
	//This needs attention!!!
	fmt.Println(stu1.Name, stu1.Age)

}

result:

5 reflect.Value structure

Just take a simple look.

type Value struct {
	typ *rtype // Type structure of variable
	ptr unsafe.Pointer // Data pointer
	flag uintptr // Flag bit
}

6 three laws of official reflection of go language

The official made an abstract description of the reflective function of Go language and summarized three laws:

  1. Reflection goes from interface value to reflection object.
  2. Reflection goes from reflection object to interface value.
  3. To modify a reflection object, the value must be settable.
  • 1) The first law means that reflection converts interface variables into reflection objects Type and Value.
func TypeOf(v interface{}) Type 
func ValueOf(v interface{}) Value
  • 2) The second law means that reflection can be restored to the original interface variable through the reflection object Value, which refers to Value
    The Interface() method provided by the struct.
func (v Value) Interface() interface{}
  • 3) The function of the third law is not well understood. It means that you want to use the reflection function to modify the value of a variable. The premise is this
    The value can be modified.
    A variable of Value type cannot be modified through reflection, because before reflection, the Value variable needs to be converted into an interface variable when transmitting parameters, and the Value content will be shallow copied. The data memory address pointed to by the reflection object Value is not the memory address of the original variable, but the memory address after copying. This means that if the Value type variable can be modified through the reflection function, the modification operation will not affect the Value of the original variable at all. Therefore, the reflect package directly prohibits the modification of variables of Value type through reflection.

For example:

package main

import (
	"fmt"
	"reflect"
)

func test1() {
	var s int = 42
	var v = reflect.ValueOf(s)
	v.SetInt(43)
	fmt.Println(s)
}
func test2() {
	var s int = 42
	// Reflection pointer type
	var v = reflect.ValueOf(&s)
	// Take out the element pointed to by the pointer and modify it
	v.Elem().SetInt(43)
	fmt.Println(s)
}
func main() {
	test1()
	test2()
}

As a result, an error will be reported in the number of rows corresponding to the set value, for example, test1 here:

7 test the calculation efficiency with and without reflection

package main

import (
	"fmt"
	"reflect"
	"time"
)

// Test the efficiency of one million calculations with and without reflection
const N = 1000000

func test1() {
	var sum = 0
	t0 := time.Now()

	for i := 0; i < (N); i++ {
		var s int = 42
		// Reflection pointer type
		var v = reflect.ValueOf(&s)
		// Take out the element pointed to by the pointer and modify it
		v.Elem().SetInt(43)
		sum += s
	}

	elapsed := time.Since(t0)
	fmt.Println("Reflection assignment ", "N:", N, "time:", elapsed, ", sum:", sum)
}

func test2() {
	var sum = 0
	t0 := time.Now()

	for i := 0; i < (N); i++ {
		var s int = 42
		s = 43
		sum += s
	}

	elapsed := time.Since(t0)
	fmt.Println("Direct assignment ", "N:", N, "time:", elapsed, ", sum:", sum)
}
func main() {
	//test1() / / reflection assignment n: 1000000 time: 33.0614ms, sum: 430000000
	test2() //Direct assignment N: 1000000 time: 953.8 µ s, sum: 430000000
}

The following results show that if there are many calculations or the core code requires high efficiency, do not use reflection to calculate, otherwise the efficiency will be greatly reduced.

test1()// Reflection assignment n: 1000000 time: 33.0614ms, sum: 430000000
test2() //Direct assignment N: 1000000 time: 953.8 µ s, sum: 430000000

Topics: Go Back-end