[Go Programming Learning] Reflection Mechanism

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 Typeused to represent a type Go, packet reflection Valueprovides 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

reference

https://github.com/datawhalechina/go-talent

Getting started with Go

Guess you like

Origin blog.csdn.net/i0o0iW/article/details/111651516