Hello everyone, my name is Xie Wei, I am a programmer.

We will continue to update the learning content of the built-in library in the near future. The main references are official documentation and source code.

The topic of this section is reflection — the ability of a type to use some mechanism to describe and detect its own behavior, simply to obtain the type and value of data.

So the core of reflection has two aspects: type and value.

Outline:

  • Self – summed up the use of reflection
  • The official use of reflection
  • What you learned

Self – summed up the use of reflection

Since the use of reflection has two aspects, there are two aspects of everyday coding: getting the type and getting the value

The most basic usage is shown below, and is used with structures.

  • reflect.TypeOf
  • reflect.ValueOf
var number int
number = 1
fmt.Println(reflect.TypeOf(number), reflect.ValueOf(number))

>> int 1
Copy the code
type numberExample int
var numberOther numberExample
numberOther = 2
fmt.Println(reflect.TypeOf(numberOther), reflect.ValueOf(numberOther), reflect.ValueOf(numberOther).Kind())

>> main.numberExample 2 int
Copy the code

You can see how to get data types, and you can see the difference between TypeOf and Kind. TypeOf gets basic data types and types defined as type. Kind fetches the underlying basic data types, but does not include types defined as type.

But the most common use of reflection is with structures. Structures in Go are collections of data of various data types, and can have their own methods.

The structure definition, as well as the tag, plays a role in the serialization and deserialization between the structure and JSON.

Define the following structure:

type Info struct {
	Name   string      `json:"name"`
	Age    interface{} `json:"age"`
	Prince float32     `json:"prince"`
}

type Groups struct {
	ID        uint     `json:"id"`
	Name      string   `json:"name"`
	Count     int      `json:"count"`
	CanFly    bool     `json:"can_fly"`
	GroupIDs  []int    `json:"group_ids"`
	GroupName []string `json:"group_name"`
	Info      `json:"info"`
}

Copy the code

Two structures are defined, and the corresponding fields are also declared with tags.

Initialization:


// An anonymous structure that defines global variables

var globalValue struct {
	groups Groups
}

var valid struct {
	tag   string
	value Model
}

func init(a) {
	globalValue.groups = Groups{
		ID:        1,
		Name:      "xieWei",
		Count:     12,
		CanFly:    false,
		GroupIDs:  []int{100.200.300.400},
		GroupName: []string{"what"."how"."when"."why"},
		Info: Info{
			Name:   "XieXiaoLu",
			Age:    20,
			Prince: 1.2345,
		},
	}

	valid.tag = "xieWei"

	valid.value = Model{
		ID:    1,
		Count: 2000,
		Name:  "Golang",}}Copy the code

Define two methods for the structure, the main operation type and the value.


func (g Groups) TypeHandler(a){}func (g Groups) ValueHandler(a){}Copy the code

A reflection operation on a structure gets the type, value, and tag of the structure’s attributes.

Think for a moment about what the author would design to get an attribute’s type, value, and tag.

Get attributes, traverse attributes, number of attributes, get attributes by index

func (g Groups) TypeHandler() {}Copy the code
func (g Groups) TypeHandler(a) {
	typeGroups := reflect.TypeOf(g)
	name := typeGroups.Name()
	fmt.Println("Name: ", name, "Kind", typeGroups.Kind())
	for i := 0; i < typeGroups.NumField(); i++ {
		filed := typeGroups.Field(i)
		fmt.Println(filed.Name, "\t", filed.Tag, "\t", reflect.ValueOf(filed), "\t", filed.Type)
	}

	for i := 0; i < typeGroups.NumField(); i++ {
		filedByIndex := typeGroups.FieldByIndex([]int{i})
		filedByName, _ := typeGroups.FieldByName(filedByIndex.Name)
		fmt.Println(filedByIndex, filedByIndex.Name, filedByIndex.Type)
		fmt.Println(filedByName, filedByName.Name, filedByName.Type)
	}

	for i := 0; i < typeGroups.NumMethod(); i++ {
		method := typeGroups.Method(i)
		fmt.Println(method.Name, method.Type)
	}
}
Copy the code

Method of manipulating structure:


	for i := 0; i < typeGroups.NumMethod(); i++ {
		method := typeGroups.Method(i)
		fmt.Println(method.Name, method.Type)
	}
Copy the code

The operating value:

func (g Groups) ValueHandler() {}Copy the code

func (g Groups) ValueHandler(a) {
	valueGroup := reflect.ValueOf(g)
	fmt.Println(valueGroup.NumField(), valueGroup.NumMethod(), valueGroup, reflect.ValueOf(&g).Elem())

	for i := 0; i < valueGroup.NumField(); i++ {
		field := valueGroup.Field(i)
		fmt.Println(field, field.Type(), field.Kind())
	}

	method := valueGroup.MethodByName("TypeHandler")
	fmt.Println(method, method.Kind(), method.Type())

	for i := 0; i < valueGroup.NumMethod(); i++ {
		method := valueGroup.Method(i)
		fmt.Println(method.Type())
	}
	ref := reflect.ValueOf(&g).Elem()

	fmt.Println(ref.FieldByName("Name"), ref.Field(0))}Copy the code

Why is this operation?

Property, value, traversal property, traversal value

The document

Why do you do that?

That of course depends on the definition of Type, which is an interface.

type Type interface {
	Method(int) Method
        MethodByName(string) (Method, bool)
        NumMethod() int
        Name() string
        Kind() Kind
        Elem() Type
        Field(i int) StructField
        FieldByIndex(index []int) StructField
        FieldByName(name string) (StructField, bool)
        FieldByNameFunc(match func(string) bool) (StructField, bool)
        NumField(a) int
}

Copy the code

You can see how to manipulate the type of the structure property.

The definition of a Value is a structure. No properties, methods.

type Value struct {
    // contains filtered or unexported fields
}
func (v Value) Field(i int) Value
func (v Value) FieldByIndex(index []int) Value
func (v Value) FieldByName(name string) Value
func (v Value) FieldByNameFunc(match func(string) bool) Value
func (v Value) Method(i int) Value
func (v Value) MethodByName(name string) Value
func (v Value) NumField(a) int
func (v Value) NumMethod(a) int
Copy the code

Sometimes, we can’t remember the API and don’t know which methods to use. What do we do?

Let’s take a structure, right?

  1. So just to review the definition of a structure, what is a structure?
    1. attribute
    2. How do I get attributes? By index, by name
    3. Number of attributes?
    4. The type of the property? The name?
  2. methods
    1. Method name
    2. How to get methods
    3. How do I call a method?
    4. Number of methods

As you can see, strictly speaking, the knowledge of the structure is property (private, public), method (call, declaration).

Therefore, the author’s underlying structure definition is also about these operations.

So far, we have not operated on the tag of the structure.

For example, the following structure definition:


type Model struct {
	ID     uint   `xieWei:"number,max=10,min=1"`
	Name   string `xieWei:"string"`
	Count  int    `xieWei:"number,max=100,min=1"`
	CanFly bool   `xieWei:"bool,default=false"`
}
Copy the code

We often see the use of these tags in GIN or GORM.

For example: Gin

type PostParam string {
    ID uint `form:"id" binding:"required,omitempty"`
    Name string `form:"name" binding:"required"`
    Number int `form:"number" binding:"required,eq=1|eq=2"`
}

Copy the code

Whether the field is required, whether the null value is omitted, and the value range are specified according to the tag above

Another example: GORM defines database tables


type Student struct {
    Name string `gorm:"type:"varchar,column:name" json:"name"`
    Number int `gorm:"type:"integer,column:number" json:"number"`
}


Copy the code

Table column name (tag);

How does that work?

Answer: Reflection

By reflection, we get the tag of the structure, which is a string, and we do operations on the string, like split operations, get the type and so on.

For example, we need to check the type of the structure’s properties ourselves.

func (m Model) Handler(name string) bool {

	typeModel := reflect.TypeOf(m)
	if tag, ok := typeModel.FieldByName(name); ok {
		if ok := strings.HasPrefix(string(tag.Tag), valid.tag); ok {
			//fmt.Println(validTagList[0])
			validTagList := strings.FieldsFunc(string(tag.Tag), func(r rune) bool {
				return r == ', ' || r == '"'
			})
			switch validTagList[1] {
			case "number":
				{
					fmt.Println(validTagList[1:)}case "string":
				fmt.Println(validTagList[1:)case "bool":
				fmt.Println(validTagList[1:)}}else {
			return false}}return false
}

>>
[number max=10 min=1]
[string]
[number max=100 min=1]
[bool default=false]
[number min=1 max=1000]
Copy the code

Perform subsequent operations.

The overall idea is:

  • Gets the tag for the structure attribute
  • Operate on tag as a string
  • Of course, their own calibration, the best structured, such asvalid:number,max=10,min=1, unified according to such operation, convenient perhaps analysis.

Conclusion:

Reflect is an application’s ability to check for its own type. Use the reflect library to get the type and value of a variable, structure, and set the corresponding value.

Reflection on structures is one of the more central uses of Reflect.

How to operate:

  • A structure has properties (public and private) and methods
  • Reflection retrieves properties, either by traversal, by index value, or by property name
  • Reflection retrieves methods, either by variable or by method name

What did you learn?

Postscript: learning, always want to master all knowledge in one breath, actually not scientific, the first time to see, you may only master 10%, the correct approach, should be repeatedly see, especially when you need to solve the problem. Finally, be sure to integrate your own thinking, just painting and writing can give you more memory information.