
This article is Golang reflection - Part 1 It mainly introduces some practical uses of reflection
1. Judge type interface.Type
The usage of using type assertions to determine data types is as follows
package main import "fmt" func main() { var s interface{} = "abc" switch s.(type) { case string: fmt.Println("s.type=string") case int: fmt.Println("s.type=int") case bool: fmt.Println("s.type=bool") default: fmt.Println("Unknown type") } }
Problems in judging the above types
- Type judgment will write a lot, and the code is very long
- Types can also be added or deleted, which is not flexible
If you use reflection to get information inside a variable
- The reflect package provides ValueOf and TypeOf
- reflect.ValueOf: get the value of the data in the input interface. If it is empty, return 0
- reflect.TypeOf: gets the type of value in the input interface. If it is empty, it returns nil
- TypeOf can pass in all types because all types implement empty interfaces
package main import ( "fmt" "reflect" ) func main() { var s interface{} = "abc" //TypeOf returns the target object reflectType:=reflect.TypeOf(s) reflectValue:=reflect.ValueOf(s) fmt.Printf("[typeof:%v]\n", reflectType) // string fmt.Printf("[valueof:%v]\n", reflectValue) // abc }
2. Reflection of custom struct
User defined struct related operations
- For member variables
- First get the reflect.Type of the interface, and then traverse NumField
- Then get the Field name and type through the Field of reflect.Type
- Finally, obtain the corresponding value through the Field interface
- For method
- First get the reflect.Type of the interface, and then traverse the NumMethod
- Then get the real method name through t.Method of reflect.Type
- Finally, get the Type and value of the method through Name and Type
Attention
- Used to traverse an unknown type, probe its Field, and abstract it into a function
- In go language, struct member variables are lowercase, and panic() is used directly when reflecting
- The structure method name is lowercase, and the reflection value will not be viewed
- Pointer methods cannot be reflected
package main import ( "fmt" "reflect" ) type Person struct { Name string Age int } type Student struct { Person // Anonymous structure nesting StudentId int SchoolName string Graduated bool Hobbies []string //panic: reflect.Value.Interface: cannot return value obtained from unexported field or method //hobbies []string Label map[string]string } func (s *Student) GoHome() { fmt.Printf("go back home,sid:%d\n", s.StudentId) } //func (s Student) GoHome() { // fmt.Printf("home, sid:%d\n", s.StudentId) //} func (s Student) GotoSchool() { fmt.Printf("School,sid:%d\n", s.StudentId) } func (s *Student) graduated() { fmt.Printf("Graduated,sid:%d\n", s.StudentId) } //func (s Student) Ggraduated() { // fmt.Printf("graduated, sid:%d\n", s.StudentId) //} func reflectProbeStruct(s interface{}) { // Get target object t := reflect.TypeOf(s) fmt.Printf("The type name of the object %s\n", t.Name()) // Gets the value type of the target object v := reflect.ValueOf(s) // Traversal to get member variables for i := 0; i < t.NumField(); i++ { // Field represents the field name of the object key := t.Field(i) value := v.Field(i).Interface() // field if key.Anonymous { fmt.Printf("Anonymous field No %d Field name %s, Field type %v, The value of the field %v\n", i+1, key.Name, key.Type, value) } else { fmt.Printf("Named field No %d Field name %s, Field type %v, The value of the field %v\n", i+1, key.Name, key.Type, value) } } // Printing method for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("The first %d Method name %s, Method type %v\n", i+1, m.Name, m.Type) } } func main() { s := Student{ Person: Person{ "geek", 24, }, StudentId: 123, SchoolName: "Beijing University", Graduated: true, Hobbies: []string{"sing", "jump", "Rap"}, //Hobbies: [] string {"sing", "jump", "Rap"}, Label: map[string]string{"k1": "v1", "k2": "v2"}, } p := Person{ Name: "Zhang San", Age: 100, } reflectProbeStruct(s) reflectProbeStruct(p) /* Object's type name Student Anonymous field the first field, field name Person, field type main.Person, field value {geek 24} Name the second field, field name StudentId, field type int, field value 123 Name the third field, the field name is SchoolName, the field type is string, and the field value is Beijing University Name the fourth field of the field, the field name is graded, the field type is bool, and the value of the field is true Name the fifth field, field name Hobbies, field type [] string, field value [Rap] Name the sixth field of the field, field name Label, field type map[string]string, field value map [K1: V1, K2: V2] The first method, method name GotoSchool, method type func(main.Student) The type name of the object is Person Name the first field, field name name, field type string, and field value Zhang San Name the second field, field name Age, field type int, field value 100 */ }
3. Structure labels and Reflections
- json tag parses json
- The label of yaml parses yaml
- The tags of xorm and gorm identify the database db field
- Custom label
- The principle is t.Field.Tag.Lookup("tag name")
Example
package main import ( "encoding/json" "fmt" "gopkg.in/yaml.v2" "io/ioutil" ) type Person struct { Name string `json:"name" yaml:"yaml_name"` Age int `json:"age" yaml:"yaml_age"` City string `json:"city" yaml:"yaml_city"` //City string `json:"-" yaml:"yaml_city" ` / / ignore json: "-" } // json parsing func jsonWork() { // Marshal object into string p := Person{ Name: "geek", Age: 24, City: "Beijing", } data, err := json.Marshal(p) if err != nil { fmt.Printf("json.marshal.err: %v\n", err) } fmt.Printf("person.marshal.res: %v\n", string(data)) // Parse from string to struct p2str := `{ "name": "Zhang San", "age": 38, "city": "Shandong" }` var p2 Person err = json.Unmarshal([]byte(p2str), &p2) if err != nil { fmt.Printf("json.unmarshal.err: %v\n", err) return } fmt.Printf("person.unmarshal.res: %v\n", p2) } // yaml parsing func yamlWork() { filename := "a.yaml" content, err := ioutil.ReadFile(filename) if err != nil { fmt.Printf("ioutil.ReadFile.err: %v\n", err) return } p := &Person{} //err = yaml.Unmarshal([]byte(content), p) err = yaml.UnmarshalStrict([]byte(content), p) // Strict parsing, consider redundant fields, ignore fields, etc if err != nil { fmt.Printf("yaml.UnmarshalStrict.err: %v\n", err) return } fmt.Printf("yaml.UnmarshalStrict.res: %v\n", p) } func main() { jsonWork() /* person.marshal.res: {"name":"geek","age":24,"city":"Beijing"} person.unmarshal.res: {Zhang San 38 Shandong} */ yamlWork() /* yaml.UnmarshalStrict.res: &{Li Si 18 Shanghai} */ }
Parsed yaml content
yaml_name: Li Si yaml_age: 18 yaml_city: Shanghai
- Custom label format resolution
package main import ( "fmt" "reflect" ) type Person struct { Name string `aa:"name"` Age int `aa:"age"` City string `aa:"city"` } // CustomParse custom parsing func CustomParse(s interface{}) { // TypeOf type r:=reflect.TypeOf(s) value := reflect.ValueOf(s) for i:=0;i<r.NumField();i++{ field:=r.Field(i) key:=field.Name if tag, ok:=field.Tag.Lookup("aa");ok{ if tag == "-"{ continue } fmt.Printf("eureka aa label, key: %v, value: %v, tag: %s\n", key, value.Field(i), tag) } } } func main() { p := Person{ Name: "geek", Age: 24, City: "Beijing", } CustomParse(p) /* aa tag found, key: Name, value: geek, tag: name aa tag found, key: Age, value: 24, tag: age aa tag found, key: City, value: Beijing, tag: city */ }
4. Reflection calling function
valueFunc := reflect.ValueOf(Add) //Function is also a data type typeFunc := reflect.TypeOf(Add) argNum := typeFunc.NumIn() //Number of function input parameters args := make([]reflect.Value, argNum) //Prepare the input parameters of the function for i := 0; i < argNum; i++ { if typeFunc.In(i).Kind() == reflect.Int { args[i] = reflect.ValueOf(3) //Assign 3 to each parameter } } sumValue := valueFunc.Call(args) //Return [] reflect.Value, because the function of go language may return a list if typeFunc.Out(0).Kind() == reflect.Int { sum := sumValue[0].Interface().(int) //Convert from Value to raw data type fmt.Printf("sum=%d\n", sum) }
5. Reflection call method
Example
user := User{ Id: 7, Name: "Jackson", Weight: 65.5, Height: 1.68, } valueUser := reflect.ValueOf(&user) //You must pass a pointer, because BMI() is a pointer method when it is defined bmiMethod := valueUser.MethodByName("BMI") //MethodByName() returns the member variable of the class through Name resultValue := bmiMethod.Call([]reflect.Value{}) //Pass an empty slice when there are no parameters result := resultValue[0].Interface().(float32) fmt.Printf("bmi=%.2f\n", result) //Think() does not use a pointer when defining. valueUser can use a pointer or not thinkMethod := valueUser.MethodByName("Think") thinkMethod.Call([]reflect.Value{}) valueUser2 := reflect.ValueOf(user) thinkMethod = valueUser2.MethodByName("Think") thinkMethod.Call([]reflect.Value{})
process
- First, get the reflection type object through reflect.ValueOf(p1)
- reflect.ValueOf(p1).MethodByName needs to pass in an accurate method name (incorrect name: panic: reflect: call of reflect.Value.Call on zero Value), and MethodByName represents registration
- [] reflect.Value this is the parameter of the method that needs to be called finally. Pass null slice without parameter
- Call call
package main import ( "fmt" "reflect" ) type Person struct { Name string Age int Gender string } func (p Person) ReflectCallFuncWithArgs(name string, age int) { fmt.Printf("A method with parameters is called, args.name: %s, args.age: %d, p.name: %s, p.age: %d\n", name, age, p.Name, p.Age, ) } func (p Person) ReflectCallFuncWithNoArgs() { fmt.Printf("A method without parameters is called\n") } func main() { p1 := Person{ Name: "geek", Age: 24, Gender: "male", } // 1. First, get the reflection value type through reflect.ValueOf(p1) getValue := reflect.ValueOf(p1) // 2. Method call with parameters methodValue1 := getValue.MethodByName("ReflectCallFuncWithArgs") // The parameter is the slice of reflect.Value args1 := []reflect.Value{reflect.ValueOf("Zhang San"), reflect.ValueOf(30)} methodValue1.Call(args1) // 3. Method call without parameters methodValue2 := getValue.MethodByName("ReflectCallFuncWithNoArgs") // The parameter is the slice of reflect.Value args2 := make([]reflect.Value, 0) methodValue2.Call(args2) /* The method with parameters is called, args.name: Zhang San, args.age: 30, p.name: geek, p.age: 24 A method without parameters is called */ }
6. Reflection creation value
6.1 create struct by reflection
t := reflect.TypeOf(User{}) value := reflect.New(t) //Create an object according to reflect.Type, get the pointer of the object, and then refer to reflect.Value according to the pointer value.Elem().FieldByName("Id").SetInt(10) value.Elem().FieldByName("Name").SetString("Song Jiang") value.Elem().FieldByName("Weight").SetFloat(78.) value.Elem().FieldByName("Height").SetFloat(168.4) user := value.Interface().(*User) //Convert reflection type to go raw data type fmt.Printf("id=%d name=%s weight=%.1f height=%.1f\n", user.Id, user.Name, user.Weight, user.Height)
6.2 create slice for reflection
var slice []User sliceType := reflect.TypeOf(slice) sliceValue := reflect.MakeSlice(sliceType, 1, 3) //reflect.MakeMap,reflect.MakeSlice,reflect.MakeChan,reflect.MakeFunc sliceValue.Index(0).Set(reflect.ValueOf(User{ Id: 8, Name: "Li Da", Weight: 80, Height: 180, })) users := sliceValue.Interface().([]User) fmt.Printf("1st user name %s\n", users[0].Name)
6.3 create map for reflection
var userMap map[int]*User mapType := reflect.TypeOf(userMap) // mapValue:=reflect.MakeMap(mapType) mapValue := reflect.MakeMapWithSize(mapType, 10) //reflect.MakeMap,reflect.MakeSlice,reflect.MakeChan,reflect.MakeFunc user := &common.User{ Id: 7, Name: "Jackson", Weight: 65.5, Height: 1.68, } key := reflect.ValueOf(user.Id) mapValue.SetMapIndex(key, reflect.ValueOf(user)) //SetMapIndex adds a key value pair to the map mapValue.MapIndex(key).Elem().FieldByName("Name").SetString("Linghu's knife") //MapIndex retrieves the corresponding map according to the Key userMap = mapValue.Interface().(map[int]*User) fmt.Printf("user name %s %s\n", userMap[7].Name, user.Name)
7. Reflection modification value
Reflection modification value must be of pointer type
Operation of modifying value: pointer.Elem().Setxxx()
package main import ( "fmt" "reflect" ) func main() { var num float64 = 3.14 fmt.Printf("Original value %f\n", num) // Get the value in num through reflect.ValueOf. You must be a pointer to modify the value //Pointer: = reflect. Valueof (Num) / / passing values directly will panic pointer := reflect.ValueOf(&num) newValue := pointer.Elem() // Assign a new value newValue.SetFloat(5.66) fmt.Printf("New value %f\n", num) }
7.1 reflection modification struct
user := User{ Id: 7, Name: "Jackson", Weight: 65.5, Height: 1.68, } valueUser := reflect.ValueOf(&user) // valueS.Elem().SetInt(8) / / panic valueUser.Elem().FieldByName("Weight").SetFloat(68.0) //FieldByName() returns the member variable of the class through Name. FieldByName cannot be called on pointer Value addrValue := valueUser.Elem().FieldByName("addr") if addrValue.CanSet() { addrValue.SetString("Beijing") } else { fmt.Println("addr It is an unexported member and cannot be exported Set") //Members beginning with lowercase letters are private members }
7.2 modifying slice by reflection
The following example indirectly implements the append function
users := make([]*User, 1, 5) //len=1,cap=5 sliceValue := reflect.ValueOf(&users) //Users are going to be modified through Value, so the address of users is passed if sliceValue.Elem().Len() > 0 { //Get the length of slice sliceValue.Elem().Index(0).Elem().FieldByName("Name").SetString("Ha ha ha") // u0 := users[0] fmt.Printf("1st user name change to %s\n", users[0].Name) } sliceValue.Elem().SetCap(3) //The new cap must be between the original len and cap sliceValue.Elem().SetLen(2) //Call the Set() function of reflect.Value to modify the original data pointed to by its underlying layer sliceValue.Elem().Index(1).Set(reflect.ValueOf(&User{ Id: 8, Name: "geek", Weight: 80, Height: 180, })) fmt.Printf("2nd user name %s\n", users[1].Name)
7.3 reflection modification map
u1 := &User{ Id: 7, Name: "Jackson", Weight: 65.5, Height: 1.68, } u2 := &User{ Id: 8, Name: "Jackson", Weight: 65.5, Height: 1.68, } userMap := make(map[int]*User, 5) userMap[u1.Id] = u1 mapValue := reflect.ValueOf(&userMap) //Prepare to modify userMap through Value, so send the address of userMap mapValue.Elem().SetMapIndex(reflect.ValueOf(u2.Id), reflect.ValueOf(u2)) //SetMapIndex adds a key value pair to the map mapValue.Elem().MapIndex(reflect.ValueOf(u1.Id)).Elem().FieldByName("Name").SetString("Linghu's knife") //MapIndex retrieves the corresponding map according to the Key for k, user := range userMap { fmt.Printf("key %d name %s\n", k, user.Name) }
See you ~