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:
- 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. - 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.
- 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
-
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
- In order to reduce the bug rate caused by writing more code, do better reduction and abstraction.
- In order to be flexible, easy to use and convenient, do dynamic parsing, calling and processing.
- 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
- Reflection related code is often difficult to read. In software engineering, code readability is also a very important index.
- 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. - 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:
- Reflection goes from interface value to reflection object.
- Reflection goes from reflection object to interface value.
- 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