13, Reflection
13.1 introduction
● reflection refers to the ability to access and modify the program itself during program operation. When the program is compiled, the variable is converted to the memory address, and the variable name will not be written to the executable part by the compiler. While running the program, the program cannot get its own information.
● languages that support reflection can integrate the reflection information of variables, such as field name, type information, structure information, etc., into the executable file during program compilation, and provide the program with an interface to access the reflection information, so that the reflection information of types can be obtained during program running and can be modified.
● the Go program uses the reflect package to access the reflection information of the program during operation. Reflection is to dynamically obtain the type information and value information of a variable star at runtime.
13.2. reflect package
● in the reflection mechanism of Go language, any interface value consists of a specific type and a specific type value. In Go language, reflection related functions are provided by the built-in reflect package. Any interface value can be understood as * * reflect Type() * * and reflect Value () consists of two parts, and the reflect package provides reflect Typeof() and reflect Valueof() two functions to get the value and type of any object.
13.2.1,TypeOf
In the Go language, use reflect The type0f() function can obtain the type object (reflet.Type) of any value, and the program can access the type information of any value through the type object.
func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v\n", v) } func main() { var a float32 = 111.111 reflectType(a) //type:float32 var b int64 = 2222 reflectType(b) //type:int64 c :=true reflectType(c) //type:bool }
13.2. 2. type name and type kind
In reflection, there are also two types of types: type and kind. In Go language, we can use the type keyword to construct many user-defined types, and kind refers to the underlying type. However, in reflection, when it is necessary to distinguish between large types such as pointers and structures, kind will be used. For example, two pointer types and two structure types are defined, and their types and types are viewed through reflection.
func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v\t kind:%v\n", v.Name(), v.Kind()) } type myInt int64 type Dog struct { name string } func main() { var a float32 = 111.111 reflectType(a) //type:float32 kind:float32 var aa *float32 reflectType(aa) //type: kind:ptr var b myInt = 2222 reflectType(b) //type:myInt kind:int64 c := true reflectType(c) //type:bool kind:bool var d rune reflectType(d) //type:int32 kind:int32 e := Dog{"Siberian Husky"} reflectType(e) //type:Dog kind:struct }
In the reflection of Go language, variables of types such as array, slice, Map and pointer have * * Name() returns null * *. The Kind types defined in the reflect package are as follows:
type Kind uint const ( Invalid Kind = iota // Illegal type Bool // Boolean Int // signed int Int8 // Signed 8-bit integer Int16 // Signed 16 bit integer Int32 // Signed 32-bit integer Int64 // Signed 64 bit integer Uint // unsigned int Uint8 // Unsigned 8-bit integer Uint16 // Unsigned 16 bit integer Uint32 // Unsigned 32-bit integer Uint64 // Unsigned 64 bit integer Uintptr // Pointer Float32 // Single-precision floating-point Float64 // Double precision floating point number Complex64 // 64 bit complex type Complex128 // 128 bit complex type Array // array Chan // passageway Func // function Interface // Interface Map // mapping Ptr // Pointer Slice // section String // character string Struct // structural morphology UnsafePointer // Bottom pointer )
13.2.3,ValueOf
reflect.Value0f() returns reflect Value type, which contains the value information of the original value. reflect.Value() and the original value can be converted to each other.
func reflectValue(x interface{}) { v := reflect.ValueOf(x) k := v.Kind() switch k { case reflect.**Int64**: // v.Int() gets the original value of the integer from the reflection, and then casts the type through int64() fmt.Printf("type is int64, value is %d\n", int64(v.Int())) case reflect.**Float32**: // v.Float() takes the original value of the integer from the reflection, and then casts the type through float32() fmt.Printf("type is float32, value is %f\n", float32(v.Float())) case reflect.**Float64**: // v.Float() takes the original value of the integer from the reflection, and then casts the type through float64() fmt.Printf("type is float64, value is %f\n", float64(v.Float())) } } func main() { var a float32 = 3.14 var b int64 = 100 reflectValue(a) // type is float32, value is 3.140000 reflectValue(b) // type is int64, value is 100 //Converts the original value of type int to reflect Value type c := reflect.ValueOf(18) fmt.Printf("type c :%T\n", c) // type c :reflect.Value }
13.3 setting the value of a variable by reflection
If you want to modify the value of a variable through reflection in a function, you need to note that the function parameters pass a copy of the value. You must pass the variable address to modify the variable value. In reflection, the special * * Elem() * * method is used to obtain the value corresponding to the pointer.
func reflectSetValue1(x interface{}) { v := reflect.ValueOf(x) if v.Kind() == reflect.**Int64** { v.SetInt(200) //If the copy is modified, the reflect package will cause panic } } func reflectSetValue2(x interface{}) { v := reflect.ValueOf(x) // In reflection, use the Elem() method to get the value corresponding to the pointer if v.Elem().Kind() == reflect.**Int64** { v.Elem().SetInt(200) } } func main() { var a int64 = 100 // reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value reflectSetValue2(&a) fmt.Println(a) //200 }
13.4. isNil() and isValid()
isNil()
func (v Value) IsNil() bool
isNil() reports whether the value held by v is nil. The classification of the value held by v must be one of channels, functions, interfaces, mappings, pointers and slices, otherwise the * * isNil() * * function will cause panic.
isValid()
func (v Value) IsValid() bool
isValid() returns whether v holds a Value. If v is Value, zero Value will return false. At this time, v methods other than isValid, String and Kind will cause panic.
● isNil() is often used to determine whether the pointer is null
● isValid() is often used to determine whether the return value is valid.
func main() { // *Null pointer of type int var a *int fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())//var a *int IsNil: true // nil value fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())//nil IsValid: false // Instantiate an anonymous structure b := struct{}{} // Try to find the "abc" field from the structure fmt.Println("Nonexistent structure member:", reflect.ValueOf(b).FieldByName("abc").IsValid())//Nonexistent structure member: false // Try to find the "abc" method from the structure fmt.Println("Nonexistent structure method:", reflect.ValueOf(b).MethodByName("abc").IsValid())//Nonexistent struct method: false // map c := map[string]int{} // Try to find a key that does not exist in the map fmt.Println("map Key not present in:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("Naza")).IsValid())//Key not present in map: false }
13.5 structural reflection
13.5. 1. Methods related to structures
Any value through reflect After typeof() obtains the information of the reflection object, if its type is a structure, you can obtain the details of the structure member through the NumField() and Field() methods of the reflection value object (reflect.Type).
reflect. Methods related to getting structure members in type:
13.5. 2. StructField type
The StructField type is used to describe the information of a field in the structure
type StructField struct { // Name is the name of the field. PkgPath is the package path of non export field, which is' 'for export field. // See http://golang.org/ref/spec#Uniqueness_of_identifiers Name string PkgPath string Type Type // Type of field Tag StructTag // Label of the field Offset uintptr // The byte offset of the field in the structure Index []int // For type Index slice at fieldbyindex Anonymous bool // Anonymous field }
13.5. 3. Structure reflection example
After using reflection to get a structure data, you can get its field information in turn through the index, or you can get the specified field information through the field name.
type students struct { Name string `json:"name"` Score int `json:"score"` } func main() { stu1 := students{ Name: "princeling", Score: 90, } t := reflect.TypeOf(stu1) fmt.Println(t.Name(), t.Kind()) // students struct // Traverse all field information of the structure through the for loop for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json")) } //name:Name index:[0] type:string json tag:name //name:Score index:[1] type:int json tag:score // Obtain the field information of the specified structure through the field name if scoreField, ok := t.FieldByName("Score"); ok { fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json")) //name:Score index:[1] type:int json tag:score } }
type students struct { Name string `json:"name" myTag:"full name"` Score int `json:"score" myTag:"fraction"` } func main() { stu1 := students{ Name: "princeling", Score: 90, } t := reflect.TypeOf(stu1) fmt.Println(t.Name(), t.Kind()) // students struct // Traverse all field information of the structure through the for loop for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("myTag")) } //name:Name index:[0] type:string json tag: name //name:Score index:[1] type:int json tag: Score // Obtain the field information of the specified structure through the field name if scoreField, ok := t.FieldByName("Score"); ok { fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json")) //name:Score index:[1] type:int json tag:score } }
13.6 summary
Reflection is a powerful and expressive tool that allows us to write more flexible code. However, reflection should not be abused for three reasons:
● reflection based code is extremely fragile. Type errors in reflection will cause panic only when it is actually running, which is likely to be a long time after the code is written.
● code that uses reflection heavily is often difficult to understand.
● the performance of reflection is low. The code based on reflection usually runs one or two orders of magnitude slower than normal code.