Reflection is the ability of a program to obtain the type and value of a variable during runtime, or to execute a variable method.
1. What is reflection
Reflection is the ability of a program to obtain the type and value of a variable during runtime, or to execute a variable method.
There are two pairs of very important functions and types in the Golang reflection package, the two functions are:
Able to obtain type information;
A runtime representation that can obtain data;
2、
Golang is a statically typing language, and reflection is based on types.
The type information of any value can be obtained through the () function.
2.1 Type Type and Kind
Types such as int32, slice, map and type keywords customized
Kind can be understood as a specific classification of types. For example, int32 and type MyInt32 are two different types, but they both belong to the int32 type.
Use () to get the variable type and type.
package main import ( "fmt" "reflect" ) func main() { type MyInt32 int32 a := MyInt32(1) b := int32(1) // (a):main.MyInt32 Kind:int32 ("(a):%v Kind:%v\n", (a), (a).Kind()) // (b):int32 Kind:int32 ("(b):%v Kind:%v\n", (b), (b).Kind()) }
From the code output, we can see that int32 and type MyInt32 are two different types, but they both belong to the int32 type.
Type definitions Click to view:
// A Kind represents the specific kind of type that a Type represents. // The zero Kind is not a valid kind. type Kind uint const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Pointer Slice String Struct UnsafePointer )
2.2 Reference to the type pointing to the element
// Elem returns a type's element type. // It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice. Elem() Type
In some cases, we need to get the type of the pointer pointing to the element, or the type of the slice element, which can be obtained by the () function.
package main import ( "fmt" "reflect" ) type myStruct struct { } func main() { a := &myStruct{} typeA := (a) // TypeOf(a):* Kind:ptr ("TypeOf(a):%v Kind:%v\n", typeA, ()) // TypeOf(a).Elem(): Elem().Kind:struct ("TypeOf(a).Elem():%v Elem().Kind:%v\n", (), ().Kind()) s := []int64{} typeS := (s) // TypeOf(s):[]int64 Kind:slice ("TypeOf(s):%v Kind:%v\n", typeS, ()) // TypeOf(s).Elem():int64 Elem().Kind:int64 ("TypeOf(s).Elem():%v Elem().Kind:%v\n", (), ().Kind()) }
From the code output, we can see that the type of reference pointing to the data can be obtained through the () function.
2.3 Structure member types
Get the number of members through NumField, and Field accesses the member type information StructField through subscripts, including member name, type, Tag information, etc.
package main import ( "fmt" "reflect" ) type secStruct struct { Cnt []int64 } type myStruct struct { Num int `json:"num_json" orm:"column:num_orm"` Desc string `json:"desc_json" orm:"column:desc_orm"` Child secStruct } func main() { s := myStruct{} typeS := (s) // Number of members // NumField:3 ("NumField:%v \n", ()) // Information of each member includes name, type, and tag for i := 0; i < (); i++ { // Access members through subscript // Field(0):{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false} // Field(1):{Name:Desc PkgPath: Type:string Tag:json:"desc_json" orm:"column:desc_orm" Offset:8 Index:[1] Anonymous:false} // Field(2):{Name:Child PkgPath: Type: Tag: Offset:24 Index:[2] Anonymous:false} ("Field(%v):%+v\n", i, (i)) } // Access members by name field, ok := ("Num") // FieldByName("Num") ok:true field:{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false} ("FieldByName(\"Num\") ok:%v field:%+v\n", ok, field) // Get tag value // json tag val:num_json ("json tag val:%+v\n", ("json")) if value, ok := ("orm"); ok { // rm tag val:column:num_orm ("orm tag val:%+v\n", value) } // Get the fields of nested structure // Cnt field:{Name:Child PkgPath: Type: Tag: Offset:24 Index:[2] Anonymous:false} ("Cnt field:%+v\n", ([]int{2})) // Cnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false} ("Cnt field:%+v\n", ([]int{2, 0})) }
3、
By obtaining the variable value and value type, the types are Array, Chan, Map, Slice or String, the length can be obtained through Len().
package main import ( "fmt" "reflect" ) func main() { b := int32(1) valueB := (b) // (b):1 Kind:int32 ("(b):%v Kind:%v\n", valueB, ()) s := "abcdefg" valueS := (s) // (s):abcdefg Kind:string Len:7 ("(s):%v Kind:%v Len:%v\n", valueS, (), ()) }
3.1 The value of the member of the structure
Similar to 2.3 structure member type obtaining the structure member type, reflect provides NumField to obtain the number of members, and Field accesses the member value through the subscript.
package main import ( "fmt" "reflect" ) type secStruct struct { Cnt []int64 } type myStruct struct { Num int `json:"num_json" orm:"column:num_orm"` Desc string `json:"desc_json" orm:"column:desc_orm"` Child secStruct } func main() { s := myStruct{ Num: 100, Desc: "desc", Child: secStruct{[]int64{1, 2, 3}}, } valueS := (s) // Number of members // NumField:3 ("NumField:%v \n", ()) // Value of each member for i := 0; i < (); i++ { // Access members through subscript // value(0):100 // value(1):desc // value(2):{Cnt:[1 2 3]} ("value(%v):%+v\n", i, (i)) } // Access members by name value := ("Num") // FieldByName("Num") value:100 ("FieldByName(\"Num\") value:%v\n", value) // Get the fields of nested structure // Cnt field:[1 2 3] ("Cnt field:%+v\n", ([]int{2, 0})) }
3.2 Traversing array and slice
The values of each element of Array, Slice, or String can be accessed through the subscript through func (v Value) Index(i int) Value.
package main import ( "fmt" "reflect" ) func main() { s := []int64{1, 2, 3, 4, 5, 6} valueS := (s) // ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6 ("ValueOf(s):%v Kind:%v Len:%v\n", valueS, (), ()) for i := 0; i < (); i++ { // (0):1 // (1):2 // (2):3 // (3):4 // (4):5 // (5):6 ("(%v):%v\n", i, (i)) } }
3.3 traversing map
reflect There are two ways to traverse map
- Iterate through maps via iterator MapIter
- First get all keys of the map, and then get the corresponding value through the key
package main import ( "fmt" "reflect" ) func main() { m := map[int]string{ 1: "1", 2: "2", 3: "3", } valueM := (m) // Iterator access iter := () for () { // key:1 val:1 // key:2 val:2 // key:3 val:3 ("key:%v val:%v\n", (), ()) } // ------ ("------") // Access via key keys := () for i := 0; i < len(keys); i++ { // key:1 val:1 // key:2 val:2 // key:3 val:3 ("key:%v val:%v\n", keys[i], (keys[i])) } }
4. Three laws of reflection
Two basic function definitions of reflection:
- Get type func TypeOf(i any) Type
- Get the value func ValueOf(i any) Value
where any is an alias for interface{}.
interface{} is an empty interface that does not contain any method signature, and any type implements an empty interface.
Therefore, interface{} can carry (value, concrete type) information of any variable.
4.1 From interface to reflection object
interface{} bears the (value, concrete type) information of the variable, and accesses the value and type of interface{} through the reflection exposure method.
It can be simply understood as the value and information of interface{} is passed to and for easy access.
4.2 From reflection object to interface
The reflected object can be converted to interface{} through the function func (v Value) Interface() (i any) , which is a reverse operation of func ValueOf(i any) Value.
package main import ( "fmt" "reflect" ) func main() { a := int32(10) valueA := (a) // ValueOf(a):10 ("ValueOf(a):%v\n", valueA) // Interface():10 ("Interface():%v\n", ()) ai, ok := ().(int32) // ok:true val:10 ("ok:%v val:%v\n", ok, ai) }
4.3 Modify an object by reflection, the object value must be modifiable
reflect provides func (v Value) CanSet() bool to determine whether the object value is modified and modify the object value through func (v Value) Set(x Value).
package main import ( "fmt" "reflect" ) func main() { a := int32(10) valueA := (a) // valueA :false ("valueA :%v\n", ()) b := int32(100) valuePtrB := (&b) // valuePtrB:false Elem:true ("valuePtrB:%v Elem:%v\n", (), ().CanSet()) ().Set((int32(200))) // b:200 Elem:200 ("b:%v Elem:%v\n", b, ()) }
This is the article about Go's implementation of obtaining the values of various types of variables through reflection. For more relevant content on obtaining variable values from Go's reflection, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!