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