082-Reflection (Structure Field Traversal)

How do I know which fields are included in an unknown structure? Using reflection, it can be done very easily.

1. Traversing the structure of the field and method

also remember the reflect.Type interface, this interface also contains these 4 methods:

type interface Type {
    ...
    NumField() int
    Field(i int) StructField

    NumMethod() int
    Method(int) Method
    ...
}

, that is, as long as you can get the Type type The interface value, you can know that the structure contains several fields, several methods. With the NumField and Method methods, you can get specific information about the i-th field and method.

!!! Note: Only the Kind can be called the Strut type, the above four methods can be called, otherwise the program will panic.

field and the method information is described by the StructField and Method types. :

type StructField struct {
    // Name is the field name.
    Name string
    // PkgPath is the package path that qualifies a lower case (unexported)
    // field name. It is empty for upper case (exported) field names.
    // See https://golang.org/ref/spec#Uniqueness_of_identifiers
    PkgPath string

    Type      Type      // field type
    Tag       StructTag // field tag string
    Offset    uintptr   // offset within struct, in bytes
    Index     []int     // index sequence for Type.FieldByIndex
    Anonymous bool      // is an embedded field
}

type Method struct {
    // Name is the method name.
    // PkgPath is the package path that qualifies a lower case (unexported)
    // method name. It is empty for upper case (exported) method names.
    // The combination of PkgPath and Name uniquely identifies a method
    // in a method set.
    // See https://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string

    Type  Type  // method type
    Func  Value // func with receiver as first argument
    Index int   // index for Type.Method
}

2. Example

package main

import (
    "fmt"
    "reflect"
)

type Data struct {
    weight uint32
    height uint32
}

// Define a structure 
type Person struct {
    Name string `tips:this is name`
    age  int32  `how old are you?`
    Data
}

// Define a method for *Person 
func (p *Person) GetName() string {
    return p.Name
}

func (p *Person) GetAge() int32 {
    return p.age
}

func main() {
    p := Person{
        "allen",
        19,
        Data{
            50,
            180,
        },
    }

    // 1. Take the type interface value t := reflect. TypeOf(p)
    fmt.Println(" field enumeration: ")
    fmt.Println("------------------------------")
    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf("index:%d\nName:%s\nPkgPath:%s\nType:%v\nTag:%s\nOffset:%v\nIndex:%v\nAnonymous:%v\n",
            i, f.Name, f.PkgPath, f.Type, f.Tag, f.Offset, f.Index, f.Anonymous)
        fmt.Println("------------------------------")
    }

    fmt.Println("method enumeration: ")
    fmt.Println("------------------------------")
    for i := 0; i < t.NumMethod(); i++ {
        m := t.Method(i)
        fmt.Printf("index:%d\nName:%s\nPkgPath:%s\nType:%v\nFunc:%v\nIndex:%v\n",
            i, m.Name, m.PkgPath, m.Type, m.Func, m.Index)
        fmt.Println("------------------------------")
    }
}

output result:

 field enumeration:
------------------------------
Index:0
Name:Name
PkgPath:
Type:string
Tag:tips:this is name
Offset:0
Index:[0]
Anonymous:false
------------------------------
index:1
Name:age
PkgPath:main
Type:int32
Tag:how old are you?
Offset:16
Index:[1]
Anonymous:false
------------------------------
index:2
Name:Data
PkgPath:
Type:main.Data
Tag:
Offset:20
Index:[2]
Anonymous:true------------------------------
Method enumeration:
------------------------------

A classmate will be very curious, why is there no way to traverse? I don't know if you still remember the knowledge about the "receiver of the method". In the above example, the method's sink is a pointer type, which means that the two methods are not defined on the Person type, but on the *Person.

The type you get with TypeOf(p) is about type information for Person, not *Person type information. Therefore, if you want to enumerate methods, you can only use TypeOf(&p) to get the type information about *Person in order to correctly enumerate the methods.

3. How to print the value corresponding to the field

The field name is also available, then how to get the value of the field?

Know that the Type interface can only get information about the type and can't get the Value information. If you want to get the field value, you can only start with the Value structure. Similarly, Value also provides four methods:

type struct Value {
    ...
}

func (v Value) NumField() int
func (v Value) Field(i int) Value
func (v Value) NumMethod() int
func (v Value) Method(i int) Value

However, the four methods and the Type method's four methods return different values, and it returns the Value type. Ok, the rest is the code code. This is really too simple, and the students can write it out.

4. Summary

  • know how to traverse the fields and methods of the structure
  • know the difference between the Type interface and the method of traversing fields and methods