Fundamentals of Go language (13)

13, Reflection

13.1 introduction

● reflection refers to the ability to access and modify the program itself during program operation. When the program is compiled, the variable is converted to the memory address, and the variable name will not be written to the executable part by the compiler. While running the program, the program cannot get its own information.

● languages that support reflection can integrate the reflection information of variables, such as field name, type information, structure information, etc., into the executable file during program compilation, and provide the program with an interface to access the reflection information, so that the reflection information of types can be obtained during program running and can be modified.

● the Go program uses the reflect package to access the reflection information of the program during operation. Reflection is to dynamically obtain the type information and value information of a variable star at runtime.

13.2. reflect package

● in the reflection mechanism of Go language, any interface value consists of a specific type and a specific type value. In Go language, reflection related functions are provided by the built-in reflect package. Any interface value can be understood as * * reflect Type() * * and reflect Value () consists of two parts, and the reflect package provides reflect Typeof() and reflect Valueof() two functions to get the value and type of any object.

13.2.1,TypeOf

In the Go language, use reflect The type0f() function can obtain the type object (reflet.Type) of any value, and the program can access the type information of any value through the type object.

func reflectType(x interface{}) {
  v := reflect.TypeOf(x)
  fmt.Printf("type:%v\n", v)
}
func main() {
  var a float32 = 111.111
  reflectType(a) //type:float32
  var b int64 = 2222
  reflectType(b) //type:int64
  c :=true
  reflectType(c) //type:bool
}

13.2. 2. type name and type kind

In reflection, there are also two types of types: type and kind. In Go language, we can use the type keyword to construct many user-defined types, and kind refers to the underlying type. However, in reflection, when it is necessary to distinguish between large types such as pointers and structures, kind will be used. For example, two pointer types and two structure types are defined, and their types and types are viewed through reflection.

func reflectType(x interface{}) {
  v := reflect.TypeOf(x)
  fmt.Printf("type:%v\t kind:%v\n", v.Name(), v.Kind())
}

type myInt int64
type Dog struct {
  name string
}

func main() {
  var a float32 = 111.111
  reflectType(a) //type:float32  kind:float32
  var aa *float32
  reflectType(aa) //type:   kind:ptr
  var b myInt = 2222
  reflectType(b) //type:myInt   kind:int64
  c := true
  reflectType(c) //type:bool  kind:bool
  var d rune
  reflectType(d) //type:int32   kind:int32
  e := Dog{"Siberian Husky"}
  reflectType(e) //type:Dog  kind:struct
}

In the reflection of Go language, variables of types such as array, slice, Map and pointer have * * Name() returns null * *. The Kind types defined in the reflect package are as follows:

type Kind uint
const (
    Invalid Kind = iota  // Illegal type
    Bool                 // Boolean
    Int                  // signed int 
    Int8                 // Signed 8-bit integer
    Int16                // Signed 16 bit integer
    Int32                // Signed 32-bit integer
    Int64                // Signed 64 bit integer
    Uint                 // unsigned int 
    Uint8                // Unsigned 8-bit integer
    Uint16               // Unsigned 16 bit integer
    Uint32               // Unsigned 32-bit integer
    Uint64               // Unsigned 64 bit integer
    Uintptr              // Pointer
    Float32              // Single-precision floating-point 
    Float64              // Double precision floating point number
    Complex64            // 64 bit complex type
    Complex128           // 128 bit complex type
    Array                // array
    Chan                 // passageway
    Func                 // function
    Interface            // Interface
    Map                  // mapping
    Ptr                  // Pointer
    Slice                // section
    String               // character string
    Struct               // structural morphology
    UnsafePointer        // Bottom pointer
)

13.2.3,ValueOf

reflect.Value0f() returns reflect Value type, which contains the value information of the original value. reflect.Value() and the original value can be converted to each other.

func reflectValue(x interface{}) {
  v := reflect.ValueOf(x)
  k := v.Kind()
  switch k {
  case reflect.**Int64**:
   // v.Int() gets the original value of the integer from the reflection, and then casts the type through int64()
   fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
  case reflect.**Float32**:
   // v.Float() takes the original value of the integer from the reflection, and then casts the type through float32()
   fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
  case reflect.**Float64**:
   // v.Float() takes the original value of the integer from the reflection, and then casts the type through float64()
   fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
  }
}
func main() {
  var a float32 = 3.14
  var b int64 = 100
  reflectValue(a) // type is float32, value is 3.140000
  reflectValue(b) // type is int64, value is 100
  //Converts the original value of type int to reflect Value type
  c := reflect.ValueOf(18)
  fmt.Printf("type c :%T\n", c) // type c :reflect.Value
}

13.3 setting the value of a variable by reflection

If you want to modify the value of a variable through reflection in a function, you need to note that the function parameters pass a copy of the value. You must pass the variable address to modify the variable value. In reflection, the special * * Elem() * * method is used to obtain the value corresponding to the pointer.

func reflectSetValue1(x interface{}) {
  v := reflect.ValueOf(x)
  if v.Kind() == reflect.**Int64** {
   v.SetInt(200) //If the copy is modified, the reflect package will cause panic
  }
}
func reflectSetValue2(x interface{}) {
  v := reflect.ValueOf(x)
  // In reflection, use the Elem() method to get the value corresponding to the pointer
  if v.Elem().Kind() == reflect.**Int64** {
   v.Elem().SetInt(200)
  }
}
func main() {
  var a int64 = 100
  // reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value
  reflectSetValue2(&a)
  fmt.Println(a) //200
}

13.4. isNil() and isValid()

isNil()

func (v Value) IsNil() bool

isNil() reports whether the value held by v is nil. The classification of the value held by v must be one of channels, functions, interfaces, mappings, pointers and slices, otherwise the * * isNil() * * function will cause panic.

isValid()

func (v Value) IsValid() bool

isValid() returns whether v holds a Value. If v is Value, zero Value will return false. At this time, v methods other than isValid, String and Kind will cause panic.

● isNil() is often used to determine whether the pointer is null

● isValid() is often used to determine whether the return value is valid.

func main() {
  // *Null pointer of type int
  var a *int
  fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())//var a *int IsNil: true
  // nil value
  fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())//nil IsValid: false
  // Instantiate an anonymous structure
  b := struct{}{}
  // Try to find the "abc" field from the structure
  fmt.Println("Nonexistent structure member:", reflect.ValueOf(b).FieldByName("abc").IsValid())//Nonexistent structure member: false
  // Try to find the "abc" method from the structure
  fmt.Println("Nonexistent structure method:", reflect.ValueOf(b).MethodByName("abc").IsValid())//Nonexistent struct method: false
  // map
  c := map[string]int{}
  // Try to find a key that does not exist in the map
  fmt.Println("map Key not present in:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("Naza")).IsValid())//Key not present in map: false
}

13.5 structural reflection

13.5. 1. Methods related to structures

Any value through reflect After typeof() obtains the information of the reflection object, if its type is a structure, you can obtain the details of the structure member through the NumField() and Field() methods of the reflection value object (reflect.Type).

reflect. Methods related to getting structure members in type:

13.5. 2. StructField type

The StructField type is used to describe the information of a field in the structure

type StructField struct {
  // Name is the name of the field. PkgPath is the package path of non export field, which is' 'for export field.
  // See http://golang.org/ref/spec#Uniqueness_of_identifiers
  Name   string
  PkgPath string
  Type    Type    // Type of field
  Tag    StructTag // Label of the field
  Offset   uintptr  // The byte offset of the field in the structure
  Index   []int   // For type Index slice at fieldbyindex
  Anonymous bool    // Anonymous field
}

13.5. 3. Structure reflection example

After using reflection to get a structure data, you can get its field information in turn through the index, or you can get the specified field information through the field name.

type students struct {
  Name  string `json:"name"`
  Score int   `json:"score"`
}

func main() {
  stu1 := students{
   Name:  "princeling",
   Score: 90,
  }

  t := reflect.TypeOf(stu1)
  fmt.Println(t.Name(), t.Kind()) // students struct
  // Traverse all field information of the structure through the for loop
  for i := 0; i < t.NumField(); i++ {
   field := t.Field(i)
   fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
  }
  //name:Name index:[0] type:string json tag:name
  //name:Score index:[1] type:int json tag:score
  // Obtain the field information of the specified structure through the field name
  if scoreField, ok := t.FieldByName("Score"); ok {
   fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
   //name:Score index:[1] type:int json tag:score
  }
}
type students struct {
  Name  string `json:"name" myTag:"full name"`
  Score int   `json:"score" myTag:"fraction"`
}

func main() {
  stu1 := students{
   Name:  "princeling",
   Score: 90,
  }

  t := reflect.TypeOf(stu1)
  fmt.Println(t.Name(), t.Kind()) // students struct
  // Traverse all field information of the structure through the for loop
  for i := 0; i < t.NumField(); i++ {
   field := t.Field(i)
   fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("myTag"))
  }
  //name:Name index:[0] type:string json tag: name
  //name:Score index:[1] type:int json tag: Score
  // Obtain the field information of the specified structure through the field name
  if scoreField, ok := t.FieldByName("Score"); ok {
   fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
   //name:Score index:[1] type:int json tag:score
  }
}

13.6 summary

Reflection is a powerful and expressive tool that allows us to write more flexible code. However, reflection should not be abused for three reasons:

● reflection based code is extremely fragile. Type errors in reflection will cause panic only when it is actually running, which is likely to be a long time after the code is written.

● code that uses reflection heavily is often difficult to understand.

● the performance of reflection is low. The code based on reflection usually runs one or two orders of magnitude slower than normal code.

Keywords: Go

Added by Venkatesh on Wed, 29 Dec 2021 14:07:41 +0200