Article Directory
What is reflection
Reflection refers to the ability of a program to access, detect, and modify its own state or behavior.
Go language provides a mechanism to update variables and check their values and call them at runtime, but the specific types of these variables are not known at compile time. This is called a reflection mechanism. This is especially useful for packages without source code. This is a powerful tool and should be avoided or used with care unless it is really necessary.
Realization of reflection
The reflection basis of Go is the interface and type system, and the reflection mechanism of Go is carried out through the interface.
The Go language defines various types in the reflect package and implements various functions of reflection, through which you can detect type information and change the value of the type at runtime.
Three laws of reflection
1. Reflection can convert "interface type variables" to "reflection type objects".
The most basic type of information is the variable and value: the reflection packet Type
used to represent a type Go, packet reflection Value
provides reflective interfaces value Go.
package main
import (
"fmt"
"reflect"
)
func main() {
var Num float64 = 3.14
//func TypeOf(i interface{}) Type
//func ValueOf(i interface{}) Value
v := reflect.ValueOf(Num)
t := reflect.TypeOf(Num)
fmt.Println("Reflect : Num.Value = ", v)
fmt.Println("Reflect : Num.Type = ", t)
}
The above example uses reflect.ValueOf and reflect.TypeOf to convert interface type variables into reflection type objects v and t, respectively, where v is the value of Num, and t is also the type of Num.
The parameter types of these two functions are empty interfaces. In the whole process, when we call reflect.TypeOf(x), when we call reflect.TypeOf(x), Num will be stored in this empty interface , And then reflect.TypeOf disassembles the empty interface and converts the interface type variable to the reflection type variable
2. Reflection can convert "reflection type objects" into "interface type variables".
According to a variable of type reflect.Value, we can use the Interface method to restore the value of its interface type.
package main
import (
"fmt"
"reflect"
)
func main() {
var Num = 3.14
v := reflect.ValueOf(Num)
t := reflect.TypeOf(Num)
fmt.Println(v)
fmt.Println(t)
origin := v.Interface().(float64)
fmt.Println(origin)
}
//3.14
//float64
//3.14
3. If you want to modify the "reflection type object", its value must be "writable".
package main
import (
"reflect"
)
func main() {
var Num float64 = 3.14
v := reflect.ValueOf(Num)
v.SetFloat(6.18)
}
In the above program, the reflect.ValueOf(x) function creates v by passing a Num copy. The reflection object v contains the copy value, so it cannot be modified.
We can use the CanSet function to determine whether the reflection object can be modified, as follows:
package main
import (
"fmt"
"reflect"
)
func main() {
var Num float64 = 3.14
v := reflect.ValueOf(Num)
fmt.Println("v的可写性:", v.CanSet())
}
If the change of v can be applied to Num, the address of Num must be passed v = reflect.ValueOf(&Num).
Through Type (), we can see that the type of v is *float64 and it is still not settable.
In fact, all reflect.Value returned by reflect.ValueOf(x) is an unacceptable address.
To make it settable, we need to use the Elem() function, which is generated by dereference and points to another variable, so it is a desirable address.
package main
import (
"fmt"
"reflect"
)
func main() {
var Num float64 = 3.14
v := reflect.ValueOf(Num)
// setting a value:
// v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value
fmt.Println("settability of v:", v.CanSet())
v = reflect.ValueOf(&Num) // Note: take the address of x.
fmt.Println("type of v:", v.Type())
fmt.Println("settability of v:", v.CanSet())
v = v.Elem()
fmt.Println("The Elem of v is: ", v)
fmt.Println("settability of v:", v.CanSet())
v.SetFloat(3.1415) // this works!
fmt.Println(v.Interface())
fmt.Println(v)
}
/*
settability of v: false
type of v: *float64
settability of v: false
The Elem of v is: 3.14
settability of v: true
3.1415
3.1415
*/
summary
1. The reflection object contains the value and type stored in the interface variable.
2. If the value contained in the reflection object is the original value, then the original value can be modified through the reflection object;
3. If the value contained in the reflection object is not the original value (the reflection object contains a copy value or an address pointing to the original value), the reflection object cannot be modified.
The role of reflection
1. When writing a function of variable parameter type, or when too many types are passed in
Typical application is object-relational mapping
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
Num int `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
Address string `gorm:"index:addr"` // create index with name `addr` for address
IgnoreMe int `gorm:"-"` // ignore this field
}
var users []User
db.Find(&users)
2. Not sure which function to call, it needs to be dynamically executed according to certain conditions
func bridge(funcPtr interface{
}, args ...interface{
})
The first parameter funcPtr is passed into the function pointer in the form of an interface, and the function parameter args is passed in as a variable parameter. In the bridge function, reflection can be used to dynamically execute the funcPtr function.
Examples of reflection
1. Modify content through reflection
As described in the previous part, it is achieved through the Elem() operation
2. Invoke methods through reflection
package main
import (
"fmt"
"reflect"
)
func hello() {
fmt.Println("Hello world!")
}
func main() {
hl := hello
fv := reflect.ValueOf(hl)
fv.Call(nil)
}
Reflection will make code execution efficiency slower for the following reasons
1. It involves memory allocation and subsequent garbage collection
2. There are a large number of enumerations in the reflect implementation, that is, for loops, such as types