Golang Reflex - Next

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~

Posted by blckspder on Tue, 23 Nov 2021 10:43:15 -0800