Golang reflection operation

Keywords: Go

preface

What is reflection? We often use it, and it's a rotten noun. I won't introduce it in detail here

In short, there is a variable of unknown type, its type can be obtained through reflection, and its properties and methods can be manipulated

Reflection is generally used as a generation tool method. For example, you need a ToString method to convert a variable to a string type. If there is no reflection, you need to write: tostringint, tostrinbool... Etc. each type needs to add a method. With reflection, you only need a ToString method. No matter what type of variable it is, just throw it to him

For a weakly typed language such as PHP, if you want to call the $B method of variable $a, you only need $a - > $b(). For a strongly typed language such as Golang, you can't be so casual. So here's a brief introduction to the application of reflection in Golang

I hope that after reading the usage of reflection, I will at least look at the relevant code in the future so as not to be confused. I can even do it myself when necessary

use

The operations reflected in Golang are defined in the package reflect. This package mainly includes the following two objects:

  • reflect.Type is used to get the type information of the variable
  • reflect.Value is used to operate on the value of a variable

Official document address: reflect.Type reflect.Value

Our use of reflection is also based on these two objects

For the use of reflection, in fact, we mainly use the following operations in ordinary use. Most complex operations are rare in a century:

  • Operation of variable types and properties
  • Operation of variable methods

The following is a simple demonstration based on these two operations

Operation of variable types and properties

Get attribute information

In reflection, type information is obtained through the reflect.Type object

Get type

u := struct {
  name    string
}{}
// Get reflection object information. I.e. reflection.type object
typeOf := reflect.TypeOf(u)
fmt.Println(typeOf)
// Type name of variable User
fmt.Println(typeOf.Name())
// Gets the underlying type of the variable 
// The underlying type of the base type is itself, such as int
// For all custom structures, the underlying type is struct
// All pointers and underlying types are ptr
// All the underlying types of golang are defined in the reflect/type.go file 
// You can locate through the reflect.Array constant
fmt.Println(typeOf.Kind())

However, don't be too happy. If you change the variable U into a pointer: u: = & user {}. You can't get the type of the variable by using the above method. Because the variable content stores the address, you need to take the value of the address. The value taking operation method in the reflection package is: reflect.TypeOf(u).Elem(). After you get the value, it's the same

It's not just pointers, including Array, Chan, Map, Ptr, Slice, etc. in fact, they store addresses, so they all need to take values

Note that the underlying type Kind here is very useful. When processing through reflection, there are too many user-defined types to judge at all, but there are more than a dozen underlying types Kind. The same processing method can be used for the structure of the same underlying type

structural morphology

type User struct {
  Gender int
}
u := struct {
  name    string
  Age     int
  Address struct {
    City    string
    Country string
  } `json:"address"`
  User
}{}

/* Get reflection object information */
typeOf := reflect.TypeOf(u)
// Number of structure fields
fmt.Println(typeOf.NumField())
// Get the information of the 0th field and return the StructField object (described below this object)
// You can get the Name and other information of the field, including the Type object of the field
fmt.Println(typeOf.Field(0))
// Get field by variable name
fmt.Println(typeOf.FieldByName("name"))
// Get the 0th element of the 2nd structure
fmt.Println(typeOf.FieldByIndex([]int{2, 0}))

/* StructField Field object content */
structField := typeOf.Field(2)
// Field name
fmt.Println(structField.Name)
// The accessible package name of the field
// All public fields starting with uppercase letters can be accessed, so this value is empty
fmt.Println(structField.PkgPath)
// reflect.Type object
fmt.Println(structField.Type)
// The tag string of the field is the ` ` string followed
// Returns the StructTag object, which is described below
fmt.Println(structField.Tag)
// The offset of the field in the memory structure of the structure, in bytes
fmt.Println(structField.Offset)
// The index of the field in the structure
fmt.Println(structField.Index)
// Anonymous field. The Gender in the structure is an anonymous field
fmt.Println(structField.Anonymous)

/* StructTag Label content */
tag := structField.Tag
// Gets the tag value of the specified name. If it does not exist, it returns an empty string
fmt.Println(tag.Get("json"))
// The difference from the Get method is that the second parameter returns whether the tag exists
// This method can be used to get the empty string of some tags with different undefined behavior
fmt.Println(tag.Lookup("json"))

array

The underlying type of Slice is Slice, but the object types stored in different slices are different

To put it bluntly, an array is actually a pointer to the first address. So if you want to get the contents of array elements, you can do a value operation

l := []int{1, 2}

typeOf := reflect.TypeOf(l)
// Null, why is null here? As mentioned above, an array is a pointer
fmt.Println(typeOf.Name())
// slice
fmt.Println(typeOf.Kind())
// Gets the type of the array element
fmt.Println(typeOf.Elem().Kind())
fmt.Println(typeOf.Elem().Name())

If a structure is stored in the array, it's good to use it as a structure

map

m := map[string]int{
  "a": 1,
}
typeOf := reflect.TypeOf(m)
// You can print the name map[string]int without using the value of map
fmt.Println(typeOf.Name())
// Object underlying type.map
fmt.Println(typeOf.Kind())
// Gets the type of the key of the map
fmt.Println(typeOf.Key().Kind())
// Gets the type of map value
fmt.Println(typeOf.Elem().Kind())

Get property value

In reflection, all operations on values are implemented through the reflect.Value object, which is obtained through reflect.ValueOf

At the same time, all Value objects can call the Interface method to turn it back to the Interface {} object, and then convert it through type assertion

Foundation type

GO provides a corresponding method for the value of basic type, which is also very simple to use

// Value of foundation type
a := int64(3)
valueOf := reflect.ValueOf(&a)
// Take the foundation type
// Note that if it is not a related type, an error will be reported. You can view the source code
// All shaping returns int64. If int32 is needed, you can get the return value and turn it strongly
fmt.Println(valueOf.Int())
//fmt.Println(valueOf.Float())
//fmt.Println(valueOf.Uint())
// ... wait

structural morphology

If it is a user-defined structure, how to get the value? This is because all custom structures are composed of basic types

u := struct {
Name string
Age  int
}{"xiao ming", 20}
valueOf := reflect.ValueOf(u)
fmt.Println(valueOf.Field(0).String())

array

What if it's an array? It's also very simple

l := []int{1, 2, 3}

valueOf := reflect.ValueOf(l)
// Modifies the value of the specified index
fmt.Println(valueOf.Elem().Index(0))
// Get array length
fmt.Println(valueOf.Elem().Len())

map

When you get the value of a Map through reflection, you get the value object and use the value object to get the value. After all, the key and value types of a Map are not fixed

m := map[string]string{
"a": "1",
}
valueOf := reflect.ValueOf(m)
// Gets the value of the specified index
fmt.Println(valueOf.MapIndex(reflect.ValueOf("a")))
// If the Value of the specified index does not exist, a Value object with kind as Invalid will be returned
fmt.Println(valueOf.MapIndex(reflect.ValueOf("c")))
//  Get map size
fmt.Println(valueOf.Len())
// Get all the key s of the map and return the Value object list
fmt.Println(valueOf.MapKeys())
// Iterator for traversing map
mapIter := valueOf.MapRange()
mapIter.Next() // Iterate the pointer to the next line and return whether there is still data
fmt.Println(mapIter.Value())
fmt.Println(mapIter.Key())

Attribute assignment

For the assignment of basic type, the reflection.value object provides related methods, all of which start with Set

Note here that only variables of pointer type can be assigned. In fact, it is well understood that value types pass values through copying during method calls. Only by passing pointers can we find the memory address of the original value and modify it

Therefore, before assignment, we should call Kind method to judge its type. If the Value object is not created through pointer, it must not be assigned

All the following assignment operations can be linked with the value operation

Foundation type

a := int64(3)
valueOf := reflect.ValueOf(a)
// This method is used to determine whether the Value object can be assigned
valueOf.CanSet()
// Because it is a pointer, you need to take value here
valueOf.Elem().SetInt(20)
fmt.Println(a)

structural morphology

The assignment of structure is the same as that of getting attribute Value above. Use pointer to get Value object, and then assign Value to its basic type

It should be noted that only the public field of the structure can be assigned through reflection. If it is assigned to a private field, an exception will be thrown

u := struct {
  Name string
  Age  int
}{"xiao ming", 20}

valueOf := reflect.ValueOf(&u)
valueOf.Elem().Field(0).SetString("xiao hei")
fmt.Println(u)

array

The basic Set method does provide a lot, but I checked around. How to assign values to array types? So I saw this method:

func (v Value) Set(x Value)

The parameter received by this Set method is the Value object? That's it. Note that Set is a direct replacement, not an addition

l := []int{1, 2, 3}

valueOf := reflect.ValueOf(&l)
// Create an array for later assignment
// Note that the array types should be the same
setValueOf := reflect.ValueOf([]int{4, 5})
valueOf.Elem().Set(setValueOf)
fmt.Println(l)

// Modifies the value of the specified index
// Through the pointer, the value of the specified index is obtained and assigned
valueOf.Elem().Index(0).SetInt(9)
fmt.Println(l)

map

m := map[string]string{
  "a": "1",
}
valueOf := reflect.ValueOf(&m)
// Set for the specified key
valueOf.Elem().SetMapIndex(reflect.ValueOf("b"), reflect.ValueOf("2"))
fmt.Println(m)

Create null Value

In addition to the above assignment operations, there is another way to judge the object type. Through the method New, you can create a null Value object of the same type and return the Value type of a pointer

The advantage of this operation is that there is no need to judge the object type at all

a := int64(3)
// Create a content of the same type. A pointer is returned
fmt.Println(reflect.New(reflect.TypeOf(a)).Elem())

Operation of variable methods

Common method

Common methods refer to methods that are not attached to structures

func add(a, b int) int {
	return a + b
}

func main() {
	valueOf := reflect.ValueOf(add)
	// constructors parameters 
	paramList := []reflect.Value{reflect.ValueOf(2), reflect.ValueOf(3)}
	// Call the function. Returns an array of values
	retList := valueOf.Call(paramList)
	// Get return value
	fmt.Println(retList[0].Int())
}

Structure method

Get method information

It should be noted here that the number of methods owned by structure pointers and objects is different. See the following for details: https://hujingnb.com/archives/348

type User struct {
	Name string
}

func (u User) GetName() string {
	return u.Name
}

func (u *User) SetName(name string) {
	u.Name = name
}

func main() {

	u := User{}
	typeOf := reflect.TypeOf(&u)
	// Gets the number of methods in the structure. Private methods are not available
	fmt.Println(typeOf.NumMethod())
	// Get the 0th Method and return the Method object. It is described below
	fmt.Println(typeOf.Method(0))
	// Get according to the Method name and return the Method object
	fmt.Println(typeOf.MethodByName("GetName"))

	/* Method object */
	setNameFunc, _ := typeOf.MethodByName("GetName")
	// Method name
	fmt.Println(setNameFunc.Name)
	// Method signature
	fmt.Println(setNameFunc.Type)
	fmt.Println(setNameFunc.Index)
	// The accessible package name of the field. The public method is empty
	fmt.Println(setNameFunc.PkgPath)
}

Method call

type User struct {
	Name string
}

func (u User) GetName() string {
	return u.Name
}

func (u *User) SetName(name string) {
	u.Name = name
}

func main() {

	u := User{}
	valueOf := reflect.ValueOf(&u)
	// Gets the number of methods in the structure. Private methods are not available
	fmt.Println(valueOf.NumMethod())
	// Get the 0th Method and return the Method object. It is described below
	fmt.Println(valueOf.Method(0))
	// Get according to the Method name and return the Method object
	fmt.Println(valueOf.MethodByName("GetName"))

	/* Method object */
	setNameFunc := valueOf.MethodByName("SetName")
	// Call method
	params := []reflect.Value{reflect.ValueOf("xiao ming")}
	setNameFunc.Call(params)
	// The value of the object has changed
	fmt.Println(u)
	// Receive method return value
	getNameFunc := valueOf.MethodByName("GetName")
	fmt.Println(getNameFunc.Call([]reflect.Value{}))
}

Original address: https://hujingnb.com/archives/676

Posted by Celauran on Fri, 29 Oct 2021 06:16:16 -0700