This article is Golang Reflex - Previous The sequel to this article focuses on some uses of reflective reality
1. Judgment Type interface.Type
The usage of 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") } }
Questions of the above types of judgment
- Type judgment can be written a lot, and the code is very long
- Types will also be added or deleted, not flexible
If reflection is used to get information inside a variable
- The reflect package provides ValueOf and TypeOf
- reflect.ValueOf: Gets the value of the data in the input interface and returns 0 if it is empty
- reflect.TypeOf: Gets the type of value in the input interface, returns nil if empty
- 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 from custom struct
Customize struct-related actions
-
For member variables
- Get the reflect.Type of the interface first, then iterate through NumField
- Get the field name and type from the Field of reflect.Type
- Finally, get the corresponding value through Field interface
-
For methods
- Get the reflect.Type of the interface first, then iterate through NumMethod
- Then get the real method name by t.Method of reflect.Type
- Finally, get the type and value of the method through Name and Type
Points of Attention
- Used to traverse unknown types to detect their fields, abstracted into a function
- go language struct member variables in lower case, when reflected directly panic()
- Structural method names are not panic, and reflection values are not viewed
- Pointer methods are not visible by reflection
package main import ( "fmt" "reflect" ) type Person struct { Name string Age int } type Student struct { Person // Anonymous Structures 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("I'm home,sid:%d\n", s.StudentId) } //func (s Student) GoHome() { // fmt.Printf("Home, sid:%d\n", s.StudentId) //} func (s Student) GotoSchool() { fmt.Printf("Going to 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 the target object t := reflect.TypeOf(s) fmt.Printf("Type name of object %s\n", t.Name()) // Gets the value type of the target object v := reflect.ValueOf(s) // Traverse to get member variables for i := 0; i < t.NumField(); i++ { // Field name of field representing object key := t.Field(i) value := v.Field(i).Interface() // field if key.Anonymous { fmt.Printf("Anonymous Paragraph %d Fields, field names %s, Field type %v, Value of field %v\n", i+1, key.Name, key.Type, value) } else { fmt.Printf("Named Field No. %d Fields, field names %s, Field type %v, Value of 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("No. %d Method, 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{"singing", "jumping", "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 1st field, field name Person, field type main.Person, field value {geek 24} Named field 2nd field, field name StudentId, field type int, field value 123 Named field 3rd field, field name SchoolName, field type string, field value Beijing University Named field fourth field, field name Graduated, field type bool, field value true Named Field Fifth Field, Field Name Hobbies, Field Type []string, Field Value [Rap] Name field 6th 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) Object's Type Name Person Named Field First Field, Field Name, Field Type string, Field Value Zhang San Named Field Second Field, Field Name Age, Field Type int, Field Value 100 */ }
3. Structural Labels and Reflections
- json's tag parses out json
- Yaml tag parses out yaml
- Labels for xorm, gorm identify database db fields
- 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() { // Object Marshal as 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 structure 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, considering redundant fields, ignoring 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 Resolution 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 Call Function
valueFunc := reflect.ValueOf(Add) //Functions are also a data type typeFunc := reflect.TypeOf(Add) argNum := typeFunc.NumIn() //Number of function input parameters args := make([]reflect.Value, argNum) //Input parameters of preparation 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) //Returns []reflect.Value, because function returns in go may be a list if typeFunc.Out(0).Kind() == reflect.Int { sum := sumValue[0].Interface().(int) //From Value to Original 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) //Pointer must be passed because BMI() is the method of pointer when defined bmiMethod := valueUser.MethodByName("BMI") //MethodByName() returns member variables of a class through Name resultValue := bmiMethod.Call([]reflect.Value{}) //Pass an empty slice with no parameters result := resultValue[0].Interface().(float32) fmt.Printf("bmi=%.2f\n", result) //Think() is not defined with a pointer. valueUser can use or not with a pointer thinkMethod := valueUser.MethodByName("Think") thinkMethod.Call([]reflect.Value{}) valueUser2 := reflect.ValueOf(user) thinkMethod = valueUser2.MethodByName("Think") thinkMethod.Call([]reflect.Value{})
process
- Reflection type objects are obtained by first reflecting.ValueOf(p1)
- reflect.ValueOf(p1).MethodByName needs to pass in the exact method name (name will not panic: reflect: call of reflect.Value.Call on zero Value), MethodByName represents registration
- []reflect.Value This is the parameter that ultimately calls the method, passing empty slices without parameters
- Call 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("Called a method with parameters, 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("Called a method without parameters\n") } func main() { p1 := Person{ Name: "geek", Age: 24, Gender: "male", } // 1. First get the reflectance type by reflect.ValueOf(p1) getValue := reflect.ValueOf(p1) // 2. Method calls with parameters methodValue1 := getValue.MethodByName("ReflectCallFuncWithArgs") // The parameter is a slice of reflect.Value args1 := []reflect.Value{reflect.ValueOf("Zhang San"), reflect.ValueOf(30)} methodValue1.Call(args1) // 3. Method calls without parameters methodValue2 := getValue.MethodByName("ReflectCallFuncWithNoArgs") // The parameter is a slice of reflect.Value args2 := make([]reflect.Value, 0) methodValue2.Call(args2) /* Called a method with parameters, args.name:Zhang San, args.age:30, p.name:geek, p.age:24 Called a method without parameters */ }
6. Reflection creation value
6.1 Reflection Create Strct
t := reflect.TypeOf(User{}) value := reflect.New(t) //Create an object from reflect.Type, get a pointer to the object, and refer to reflect.Value from the pointer value.Elem().FieldByName("Id").SetInt(10) value.Elem().FieldByName("Name").SetString("Song River") 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 Reflection Create Slce
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: "Lida", Weight: 80, Height: 180, })) users := sliceValue.Interface().([]User) fmt.Printf("1st user name %s\n", users[0].Name)
6.3 Reflection Create map
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("The fox knife") //MapIndex takes out the corresponding map based on Key userMap = mapValue.Interface().(map[int]*User) fmt.Printf("user name %s %s\n", userMap[7].Name, user.Name)
7. Reflection Modified Value
Reflection modification value requirement must be pointer type
Action to modify the value: pointer.Elem().Setxxx()
package main import ( "fmt" "reflect" ) func main() { var num float64 = 3.14 fmt.Printf("Original Value %f\n", num) // To get the value in num by reflect.ValueOf, you must be a pointer to modify the value //Pointer: = reflect.ValueOf(num)//Passing a value directly panic pointer := reflect.ValueOf(&num) newValue := pointer.Elem() // Assign new values 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)//will panic valueUser.Elem().FieldByName("Weight").SetFloat(68.0) //FieldByName() returns a member variable of a 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 Is an unexposed member, not Set") //Members starting with a lowercase letter are equivalent to private members }
7.2 Reflection Modification Slce
The following example implements append indirectly
users := make([]*User, 1, 5) //len=1,cap=5 sliceValue := reflect.ValueOf(&users) //Prepare to modify users through Value, so pass the address of users if sliceValue.Elem().Len() > 0 { //Get the length of the 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 reflect.Value et() function to modify the raw data its underlying points to 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 pass userMap's address 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("The fox knife") //MapIndex takes out the corresponding map based on Key for k, user := range userMap { fmt.Printf("key %d name %s\n", k, user.Name) }
See you ~
Pay attention to the public numbers and share more original dry goods with you~