1. Introduction to Reflection

1.1 What is Reflection?

The Go language provides a mechanism for updating and checking the values of variables, calling the methods of variables, and the inherent operations that variables support at runtime, but the exact types of these variables are not known at compile time. This mechanism is called reflection. Reflection also allows us to treat the type itself as a value type of the first class.

Reflection refers to the ability to access and modify the program itself when the program is running. When the program is compiled, variables are converted to memory addresses, and variable names are not written to the executable by the compiler. When the program is running, the program cannot obtain its own information.


So, for example, usually we define the variable as ortho

var a int
Copy the code

Define the variable a as an int

Now I don’t know what type of variable A is, but I can figure out where variable A came from by reflection! What type is it!

type FanOne struct {
	name string
}
func main(a){	
	var a int = 1
	var d FanOne
	fmt.Println(reflect.TypeOf(a))  		  // int 
	// This is the type of a! Note the type! Not a category! Although this type and category are the same
	// We'll talk about the difference between Type and Kind
	fmt.Println(reflect.ValueOf(a).Kind())    //int 
	// We get the category of a, and we judge the category by the value of a
	fmt.Println(reflect.TypeOf(d))			  //main.FanOne
	// The type is main.FanOne is defined in main
	fmt.Println(reflect.ValueOf(d).Kind())    //struct
	// The class is struct
	// Print the type name and type of d. The type name is FanOne
	// FanOne is a type of structure, so the category is struct
}
Copy the code

So the category and the type are sometimes the same and sometimes different.

1.2 Why is reflection needed?

In development, when we value for a certain function of processing, but in order to ensure that the function can accept more types of value, because go is strongly typed language, although interface can accept all of the data type, but at the time of processing data, to make a different treatment for different types will appear when the code is redundant, So we can use reflection to judge and process the parameters passed in.

See examples for details

2. Reflect the package

2.1 Basic Reflection

reflect.TypeOf()			 // Get the Type of the variable and return reflect.type
reflect.ValueOf()			 // Get the Value of the variable and return type reflect.value
reflect.Value.Kind()		 // Get the category of the variable and return a constant
reflect.Value.Interface()	 // Convert to type interface{}
Copy the code

2.2 Reflection and Pointers

When you get a reflection object from a pointer, you can use reflect.elem () to get the element type to which the pointer points. This process is called fetching an element, which is equivalent to doing an * operation on a variable of the pointer type

reflect.ValueOf(xxx).Elem()
Copy the code

2.3 Reflection and Objects

Reflect.new (XXX) or reflect.zero(XXX) can be used to create objects of the original type

func CreatePrimitiveObjects(t reflect.Type) reflect.Value {
    return reflect.Zero(t)
}
Copy the code

You can also use

reflect.New()
Copy the code

To create the original object.

2.4 Reflection and function

The function can be called with reflect.value if the type of the Value in the reflect.value object is a function. To Call a function using reflection, you need to pass the arguments to the Call() method after constructing a slice of the reflection Value object [] reflect.value. When the Call is complete, the return Value of the function is returned via [] reflect.value. In reflection, both functions and methods are of Type reflect.func. To Call a function, use the Call() method of Value, for example:

package main

import (
	"fmt"
	"reflect"
)

func FanOne(a) string {
	return "Three with one button."
}

func FanOneWoW(a string) string {
	return fmt.Sprintf("%s is going to give FanOne one button three connection oh ~",a)
}

func main(a) {
	FanOneNotArgs := reflect.ValueOf(FanOne).Call([]reflect.Value{})		 / / no parameters
	FanOneHaveArgs := reflect.ValueOf(FanOneWoW).Call([]reflect.Value{reflect.ValueOf("我")})  / / a parameter
	fmt.Println(FanOneNotArgs[0])
	fmt.Println(FanOneHaveArgs[0])}Copy the code

2.5 Reflection and Methods

It will be added later.

2.6 Reflection Examples

Fill in the fn function such that the output is

Requires that no switch or if or other select statements be used.

func fn(callback interface{}, bytes []byte) {
		//coding
}

type aaa struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func Test(t *testing.T) {
		fn(func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312,", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))

	fn(func(a []aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}

		fmt.Println("12312312,", aaas[0])
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))

	fn(func(a *aaa) string {
		fmt.Println("12312312,", a)
		aaas := a
		fmt.Println("12312312,", aaas)
		return "xxxx"
	}, []byte("{\"name\":\"gsjk\",\"age\":2}"))

	fn(func(a string) string {
		fmt.Println("12312312,", a)

		aaas := a
		fmt.Println("12312312,", aaas)
		return "xxxx"
	}, []byte("\"sss\""))

	fn(func(a int) string {
		fmt.Println("-- -- -- -- -- -- -- -- -- -- -,", a)

		aaas := a
		fmt.Println("-- -- -- -- -- -- -- -- -- -- -,", aaas)
		return "xxxx"
	}, []byte("123"))}Copy the code

(1) The name of the test must have _test, otherwise it seems to report error, I am so.

go test xxx_test.go
go test -v xxx_test.go
Copy the code

(2) The second is to understand the fn() inside the anonymous function to take out separately

func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312,", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))
Copy the code

You can see that this is an array of type * AAA. So our task is to reflect the anonymous function in fn, and then call the reflected anonymous function, passing arguments to it.

(3) So let’s first ValueOf and TypeOf the interface{}, and then look at the various values of the anonymous function

func fn(callback interface{}, bytes []byte) {
	v := reflect.ValueOf(callback)  //0xbaff40
	t := reflect.TypeOf(callback)  //func([]*main.aaa) string
}
Copy the code

We can see that the Type of the function we are taking is func([]*main.aaa) string, so we can use

paramsValue := t.In(0)  //[]*main.aaa
Copy the code

Get the parameter passed to the anonymous function

(4) Key!! ParamsValue = paramsValue []*main.aaa; value = reflect.type! We want the type []*main.aaa, not the name! So we’re going to create an object of this type, and we’re going to convert it to the corresponding type

	val := reflect.New(paramsValue)
	newT := val.Interface()
	fmt.Printf("valValue:%v , valType: %T \n",val,val)    //valValue:&[] , valType: reflect.Value
	fmt.Printf("newTValue:%v , newTType: %T \n",newT,newT)//newTValue:&[] , newTType: *[]*main.aaa
Copy the code

We want to create objects of such a category, and while GO is not object-oriented programming, it can be understood here.

Why this type?

This is because the bytes slice is deserialized into a variable of this type, passed into this anonymous function!

	if v.IsValid() {                       			//function valid or not
		_ = json.Unmarshal(bytes, newT)               //byte to json
	}
Copy the code

The value passed in was of type []*main.aaa but we got the type *[]*main.aaa, which is obviously wrong.

	fmt.Printf(Callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT)}))
	fmt.Printf("*************************\n")
Copy the code

An error! Wrong type!

So we’re going to go to *. In reflection, instead of adding * to remove! The following does not work in reflection.

package main

import (
  "fmt"
  "reflect"
)

func main(a){
  var a int = 1
  var b *int = &a
  var c **int = &b
  fmt.Println(a, *b, c)
  fmt.Println(reflect.TypeOf(a))
  fmt.Println(reflect.TypeOf(*b))
  fmt.Println(reflect.TypeOf(b))
}
Copy the code

So we can get rid of this * with reflect.elem ()

	fmt.Printf(Callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT).Elem()}))
	fmt.Printf("*************************\n")
Copy the code

And we’re done!

3. Summary

In the past, I rarely used reflection, and almost never used it in the project. However, during the summer internship, the first task was to write the reflection interface, so I made up for it crazily. Reflection was really a little difficult for me to understand, and it took me two days to make it.

My original idea was to use if to determine the type, or an assertion, and then switch to determine the type, but this is not very practical, to change the name would have to change the code. For example, if you have structure AAA, it doesn’t work if you have structure BBB.

In this case, it is much more flexible to reflect an object of this type and then assign a value to it.

Learned!

Internships are painful! But I learned a lot of new knowledge! And a lot of big guys! And get paid! Also comfortable!