Hi, I’m Mingo.

In their learning Golang this time, I wrote a detailed study notes on my personal number WeChat public Go programming time, for the language, I was a beginner, so writing should fit in with the new to classmates, if you are just learning the language, don’t focus on prevention, study together, Grow together.

My online blog: golang.iswbm.com

My Github:github.com/iswbm/GolangCodingTime


When I was working with Python, I could even do something immediately with internal savings without knowing what introspection and reflection were.

However, after learning Go, reflection has become a difficulty for me, and I always feel that the concept of reflection object is extremely abstract.

Like the previous article, this article will try to use diagrams to explain some abstract concepts. If I understand something wrong, I hope you can give me a message at the end of the article to correct it. Thank you.

About the reflection of the content, I divided into several, this one is the introduction, will start from the classic three laws of reflection, write some demo code, tell you the basic content of reflection.

1. The real world versus the reflective world

In this article, to distinguish the variable value types before and after reflection, I refer to the pre-reflection environment as the real world and the post-reflection environment as the reflection world. It’s a loose analogy, but it’s helpful to me, and HOPEFULLY useful to you.

In the world of reflection, we have the ability to get the type, properties, and methods of an object.

2. Two types: Type and Value

In the world of Go reflection, there are two types that are important and central to the whole reflection thing, and you’ll need to look at them before you learn how to use the Reflect package:

  1. reflect.Type
  2. reflect.Value

They correspond to type and value in the real world, respectively, but in reflection objects, they have more content.

From the source, reflect.Type exists as an interface

type Type interface {
    Align() int
    FieldAlign() int
    Method(int) Method
    MethodByName(string) (Method, bool)
    NumMethod() int
    Name() string
    PkgPath() string
    Size() uintptr
    String() string
    Kind() Kind
    Implements(u Type) bool
    AssignableTo(u Type) bool
    ConvertibleTo(u Type) bool
    Comparable() bool
    Bits() int
    ChanDir() ChanDir
    IsVariadic() bool
    Elem() Type
    Field(i int) StructField
    FieldByIndex(index []int) StructField
    FieldByName(name string) (StructField, bool)
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type
    Key() Type
    Len() int
    NumField() int
    NumIn() int
    NumOut() int
    Out(i int) Type
    common() *rtype
    uncommon() *uncommonType
}Copy the code

And reflect.Value exists as a structure,

type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}Copy the code

At the same time, it receives many methods (see table below), which cannot be described here due to space constraints.

Addr Bool Bytes runes CanAddr CanSet Call CallSlice call Cap Close Complex Elem Field FieldByIndex FieldByName FieldByNameFunc Float Index Int CanInterface Interface InterfaceData IsNil IsValid IsZero Kind Len MapIndex MapKeys MapRange Method NumMethod MethodByName NumField OverflowComplex OverflowFloat OverflowInt OverflowUint Pointer Recv recv  Send send Set SetBool SetBytes setRunes SetComplex SetFloat SetInt SetLen SetCap SetMapIndex SetUint SetPointer SetString Slice Slice3 String TryRecv TrySend Type Uint UnsafeAddr assignTo ConvertCopy the code

From the previous section (), we learned that an interface variable is actually composed of a pair (type and data) that records the value and type of the actual variable. That is, in the real world, type and value are combined to form interface variables.

In the reflective world, type and data are separated, and they are represented by reflect. type and reflect.Value respectively.

3. Read the three laws of reflection

There are three laws of reflection in Go, and they’re important to use when you’re learning about reflection:

  1. Reflection goes from interface value to reflection object.
  2. Reflection goes from reflection object to interface value.
  3. To modify a reflection object, the value must be settable.

Translation:

  1. Reflection can convert interface type variables into “reflection type objects”;
  2. Reflection can convert a reflection type object to an interface type variable.
  3. If you want to modify a Reflection type object, its type must be writable;

The first law of

Reflection goes from interface value to reflection object.

To convert from an interface variable to a reflection object, we need to mention two important methods in the Reflect package:

  1. Reflect.typeof (I) : gets the TypeOf the interface value
  2. Reflect.valueof (I) : gets the ValueOf the interface value

The objects returned by these two methods are called reflection objects: Type Object and Value Object.

For example, how are these two methods used?

Package main import (" FMT ""reflect") func main() {var age interface{} = 25 fmt.printf (" Value: %v \n", age, age) t := reflect.typeof (age) v := reflect.valueof (age) // From interface variable to reflection object fmt.Printf(" From interface variable to reflection object: Printf(" From interface variable to reflection object: Value object of Type %T \n", v)}Copy the code

The output is as follows

The original interface variable is of Type int and has a Value of 25. From the interface variable to the reflected object: Type The object is of Type *reflect.rtype From the interface variable to the reflected object: Value The object is of Type reflect.ValueCopy the code

This completes the conversion from an interface type variable to a reflection object.

Wait, didn’t we define age above as an int? Why does the first rule say interface type?

We already mentioned this in the previous section (the three “unspoken rules” for interfaces). Because TypeOf and ValueOf accept an empty interface type, and Go functions are value-passing, Go implicitly converts our types to interface types.

// TypeOf returns the reflection Type of the value in the interface{}.TypeOf returns nil.
func TypeOf(i interface{}) Type

// ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) ValueCopy the code

The second law of

Reflection goes from reflection object to interface value.

Contrary to the first law, the second law describes the conversion from reflection objects to interface variables.

The reflect.Value constructor accepts the Interface method and returns a variable of type Interface {}. Only Value can be reversed, not Type. If Type can be reversed, what can it be?)

// Interface returns v's current value as an interface{}.
// It is equivalent to:
//    var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
    return valueInterface(v, true)
}Copy the code

This function is a bridge we use to implement the conversion of reflection objects into interface variables.

Examples are as follows

Package main import (" FMT ""reflect") func main() {var age interface{} = 25 fmt.printf (" Value: %v \n", age, age) t := reflect.typeof (age) v := reflect.valueof (age) // From interface variable to reflection object fmt.Printf(" From interface variable to reflection object: Type Object of Type %T \n", T) FMT.Printf(" From interface variable to reflection object: Value object type %T \n", v) // From reflection object to interface variable I := v.interface () fmt.printf (" From reflection object to interface variable: new object type %T Value %v \n", I, I)}Copy the code

The output is as follows

The original interface variable is of Type int and has a Value of 25. From the interface variable to the reflected object: Type The object is of Type *reflect.rtype From the interface variable to the reflected object: Value The object is of Type reflect.Value From the reflected object to the interface variable: The new object is of type int and the value is 25Copy the code

Of course, the last converted object, statically typed, is interface{}. To convert to the original primitive type, you need to convert the type assertion, which I explained in the previous section. You can click here to review :().

i := v.Interface().(int)Copy the code

So far, we’ve learned about the two laws of reflection, and I’ve drawn a picture of how these two laws are understood, and you can use the picture below to help you understand and memorize them.

The third law

To modify a reflection object, the value must be settable.

The reflective world is a “reflection” of the real world, a description I make, but it’s not strictly true, because not everything you do in the reflective world reverts to the real world.

The third law introduces the concept of settable (or writable).

As we have stated in previous articles, functions in Go are passed by value, and as long as you do not pass a pointer to a variable, changes you make inside the function do not affect the original variable.

Returning to reflection, when you use reflect.typeof and reflect.valueof, if you pass a pointer to a variable other than the interface variable, the Valueof the variable in the reflection world will always be a copy of the real world, and the changes you make to the reflection object will not be reflected in the real world.

So in the rules of reflection

  • Reflection objects that are not created by receiving Pointers to variables are not “writable”
  • Whether or not”writability“, can be usedCanSet()To get information
  • Modifying an object that is not “writable” makes no sense and is considered illegal, so an error is reported.
Package main import (" FMT ""reflect") func main() {var name string = "Go" v := reflect.valueof (name) Println(" writability is :", v.canset ())}Copy the code

The output is as follows

Writability is: falseCopy the code

To make a reflection object writable, two things need to be noted

  1. Pass a pointer to a variable when creating a reflection object
  2. useElem()The function returns the data to which the pointer points

The complete code is as follows

Package main import (" FMT ""reflect") func main() {var name string = "Go" v1 := reflect.valueof (&name) Println("v1 writable :", v1.canset ()) v2 := v1.elem () writable :", v2.canset ())}Copy the code

The output is as follows

The writability of v1 is false. The writability of v2 is trueCopy the code

Now that you know how to make an object in the reflective world writable, it’s time to see how to update it for modifications.

Reflection objects have the following methods that start with the word Set

These methods are our entry points for modifying values.

Let’s take an example

Package main import (" FMT ""reflect") func main() {var name string = "Go programming time" FMT.Println(" ", name) v1 := reflect.valueof (&name) v2 := v1.elem () v2.setString ("Python Programming time ") FMT.Println(" After updating the reflected object, the real world name becomes: ", name) }Copy the code

The output is as follows

The original value of name in the real world is: Go Programming time After updating the reflection object, name in the real world becomes: Python programming timeCopy the code

Refer to the article

  • Reflection: The Three laws of Go language reflex