This is the 20th day of my participation in the August More Text Challenge

What is reflection

Wikipedia definition of reflection:

In computer science, reflection refers to the ability of a computer program to access, detect, and modify its own state or behavior during Run time. Reflection, figuratively speaking, is the ability of a program to “observe” and modify its behavior as it runs.

We know that a variable knows its type when it is defined, but at run time, it can be transformed at any time, either explicitly or implicitly.

Its essence is that the program detects the type information and memory structure of the object at runtime. With assembly language, we don’t have this problem at all.

What is a type? I’m just trying to figure out how to resolve the zeros and ones in this space.

Reflection models vary from language to language, and some languages do not support reflection. The Go Language Bible defines reflection as follows:

The Go language provides a mechanism to update variables and check their values and call their methods at run time, but the exact types of these variables are not known at compile time. This is called reflection.

Use scenarios for reflection

The awkward thing about Go is that it doesn’t have generics. So interface is used to define “indefinite parameters”. Reflection is mostly used to resolve interface variables.

But usually the reflected code is flawed:

  • Hard to read.
  • Once there is a place that is not well judged, it is easy to panic.
  • The problem could not be found at compile time.
  • The operation speed is slow.

Expect the Go generics to come out soon.

How does Go achieve reflection

Imagine if we wanted to know the type of this variable at all times, what would we do?

Write it down.

The same goes for Go. One records taye and the other records the value, which of course is a pointer. When it needs to get the value, the corresponding function is executed according to type.

Two methods are exposed:

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
Copy the code
func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.type)
}
Copy the code
Func ValueOf(I interface{}) Value{if I == nil {return Value{}} //... Func unpackEface(I interface{}) Value {e := (*emptyInterface)(unsafe.Pointer(& I)) t  := e.typ if t == nil { return Value{} } f := flag(t.Kind()) if ifaceIndir(t) { f |= flagIndir } return Value{t, e.word, f} }Copy the code

I is converted to the *emptyInterface type, and its TYP field and word field and a flag bit field are assembled into a Value structure. This is the return Value of ValueOf, which contains the pointer to the type structure, the address of the real data, and the flag bit.

So if you go back to where you started, you’re essentially returning a type, an address, and saying how to parse that address.

Of course, this is only the tip of the iceberg, the details also look at their own source code.

The three laws of reflection

  1. Reflection goes from interface value to reflection object.
  2. Reflection goes from reflection object to interface value.

The interface variable can be obtained from the reflection object, and the interface variable can be converted to the reflection object.

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

This means that if you want to change a reflection object, its value must be modifiable.

Here’s an example:

Var x float64 = 3.4V := reflect.valueof (x) v.setFloat (7.1) // Error: Will panicCopy the code

If you look at the code in valueOf, it just converts the address of the variable that was passed in to the emptyInterface, and it returns a way of parsing that tells you what type the variable is and how you should parse it.

If we want to change the quantity, we need to go to the exact position of the variable.

func (v Value) Elem(a) Value {
   k := v.kind()
   switch k {
   case Interface:
      var eface interface{}
      if v.typ.NumMethod() == 0 {
         eface = *(*interface{})(v.ptr)
      } else {
         eface = (interface({}) (* *interface {
            M()
         })(v.ptr))
      }
      x := unpackEface(eface)
      ifx.flag ! =0 {
         x.flag |= v.flag.ro()
      }
      return x
   case Ptr:
      ptr := v.ptr
      ifv.flag&flagIndir ! =0 {
         ptr = *(*unsafe.Pointer)(ptr)
      }
      // The returned value's address is v's value.
      if ptr == nil {
         return Value{}
      }
      tt := (*ptrType)(unsafe.Pointer(v.typ))
      typ := tt.elem
      fl := v.flag&flagRO | flagIndir | flagAddr
      fl |= flag(typ.Kind())
      return Value{typ, ptr, fl}
   }
   panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
Copy the code

So you see, either interface or pointer, everything else will panic.

So:

Var x float64 = 3.14v := reflect.valueof (x) FMT.Println("settability of v:", v.CanSet()) v2 := reflect.ValueOf(&x) p := v2.Elem() fmt.Println("settability of p:", p.CanSet())Copy the code

Output:

settability of v: false
settability of p: true
Copy the code

Pick up language

See a lot of times than their own look at the source code, in fact, very few.

If you don’t understand or welcome to comment, I will answer.