Golang reflection reflect in-depth understanding and examples

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.


[TOC]



"Recorded in February 2018"



The concept of reflection in programming languages



In computer science, reflection refers to a class of applications that are self-describing and self-controlling. In other words, this kind of application uses some mechanism to realize the description (self-representation) and monitoring (examination) of its own behavior, and can adjust or modify the state and related semantics of the behavior described by the application according to the state and result of its behavior.



The reflection models are different for each language, and some languages do not support reflection at all. Golang language realizes the reflection, the reflection mechanism is the method and the property of the dynamic call object at run time, the official self-contained reflect package is the reflection correlation, as long as contains this package can use.



In one sentence, Golang's grpc is also realized by reflection.



Interface and Reflection



Before we talk about reflection, let's take a look at Golang some principles of type design


    • Variables include (type, value) two parts

      • Understand this and know why nil! = nil.
    • The type includes static type and concrete type. In simple terms, the static type is the type that you are seeing in the code (such as int, string), concrete type is what the runtime system sees

    • Whether the type assertion succeeds depends on the concrete type of the variable, not the static type. Therefore, a reader variable can also be asserted as writer by type if its concrete type also implements the Write method.


The next reflection, which is built on the type, is that the type of the variable of the specified type of Golang is static (that is, the variable that specifies int, string, whose type is the static type), which is determined when the variable is created. Reflection is primarily related to the interface type of Golang (its type is concrete type), and only the interface type has a reflection.



In the implementation of Golang, each interface variable has a corresponding pair,pair that records the value and type of the actual variable:


type)


Value is the actual variable value, and type is the actual variable. A variable of type interface{} contains 2 pointers, one pointer to the type of the value "corresponding to concrete type", and the other pointer to the actual value "corresponding to value".



For example, create a type of *os. File and assign it to an interface variable R:


tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

var r io.Reader
r = tty


The following information will be logged in the pair of the interface variable R: (TTY, *os. File), this pair is constant during the continuous assignment of the interface variable and assigns the interface variable r to the other interface variable W:


var w io.Writerw = r.(io.Writer)


The pair of the interface variable w is the same as the pair of R, all: (TTY, *os. File), even if W is an empty interface type, the pair is constant.



The existence of interface and its pair is the precondition of realizing reflection in Golang, understanding the pair, it is easier to understand the reflection. Reflection is used to detect a mechanism stored inside an interface variable (value, type concrete) pair.



Reflection reflect of Golang



Basic functions of reflect typeof and valueof



Since reflection is used to detect a mechanism stored inside an interface variable (value, type concrete) pair. So what's the way in Golang's reflect reflex package that allows us to get the information directly inside the variable? It provides two types (or two methods) that allow us to easily access the content of interface variables, respectively, reflect. ValueOf () and reflect. TypeOf (), take a look at the official explanation


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

Translation: ValueOf is used to get the value of the data in the input parameter interface. If the interface is empty, it returns 0.


// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf (i interface {}) Type {...}

Translation: TypeOf is used to dynamically obtain the type of the value in the input parameter interface, and returns nil if the interface is empty 


Reflect. TypeOf () is to get the type,reflect in the pair. ValueOf () Gets the value in the pair, as an example:


package main

import (
"fmt"
"reflect"
)

func main () {
var num float64 = 1.2345

fmt.Println ("type:", reflect.TypeOf (num))
fmt.Println ("value:", reflect.ValueOf (num))
}

operation result:
type: float64
value: 1.2345


Description


    1. Reflect. TypeOf: Gives the type we want directly, such as float64, int, various pointer, struct, etc. real types

    2. Reflect. ValueOf: Directly gives us the exact value we want, such as 1.2345, or a struct struct that resembles &{1 "Allen.wu" 25}.

    3. That is, reflection can convert an interface type variable to a reflection type object, and the reflection type refers to reflect. Type and Reflect.value are the two types of


From Relfect. Gets the information for the interface interface in value



When executing reflect. ValueOf (interface), a type of "Relfect" was obtained. The Value "variable, which can get the real content of the interface variable through its own interface () method, can then be converted to the original real type by the type judgment. However, we may be known as the original type, or it may be unknown to the original type, so the following are described in two cases.



The original type is known to be cast ""



The practice of converting a known type to its corresponding type follows, directly through the interface method and then casting, as follows:


realValue: = value.Interface (). (known type)


Examples are as follows:


package main

import (
"fmt"
"reflect"
)

func main () {
var num float64 = 1.2345

pointer: = reflect.ValueOf (& num)
value: = reflect.ValueOf (num)

// Can be understood as "forced conversion", but when you need to pay attention, when converting, if the type of conversion does not exactly match, panic
// Golang is very strict about types, and the types must fully comply
// The following two, one is * float64, the other is float64, if mixed, it will panic
convertPointer: = pointer.Interface (). (* float64)
convertValue: = value.Interface (). (float64)

fmt.Println (convertPointer)
fmt.Println (convertValue)
}

operation result:
0xc42000e238
1.2345


Description


    1. When converting, if the type of conversion is not fully compliant, then direct panic, type requirements are very strict!
    2. When converting, do you want to distinguish between a pointer or a
    3. That is, reflection can then re-convert the "Reflection type Object" to "interface type variable"


Unknown original type "traverse probing its filed"



In many cases, we may not know its specific type, so how do we do that at this time? We need to do a traversal probe of its filed to learn that the example is as follows:


package main

import (
"fmt"
"reflect"
)

type User struct {
Id int
Name string
Age int
}

func (u User) ReflectCallFunc () {
fmt.Println ("Allen.Wu ReflectCallFunc")
}

func main () {

user: = User {1, "Allen.Wu", 25}

DoFiledAndMethod (user)

}

// Get arbitrary parameters through the interface and then reveal them one by one
func DoFiledAndMethod (input interface {}) {

getType: = reflect.TypeOf (input)
fmt.Println ("get Type is:", getType.Name ())

getValue: = reflect.ValueOf (input)
fmt.Println ("get all Fields is:", getValue)

// get method field
// 1. Get the reflect.Type of the interface first, and then traverse through NumField
// 2. Then get its Field through the Field of reflect.Type
// 3. Finally get the corresponding value through Interface () of Field
for i: = 0; i <getType.NumField (); i ++ {
field: = getType.Field (i)
value: = getValue.Field (i) .Interface ()
fmt.Printf ("% s:% v =% v \ n", field.Name, field.Type, value)
}

// get method
// 1. Get the reflect.Type of the interface first, and then traverse through .NumMethod
for i: = 0; i <getType.NumMethod (); i ++ {
m: = getType.Method (i)
fmt.Printf ("% s:% v \ n", m.Name, m.Type)
}
}

operation result:
get Type is: User
get all Fields is: {1 Allen.Wu 25}
Id: int = 1
Name: string = Allen.Wu
Age: int = 25
ReflectCallFunc: func (main.User)


Description



The steps to get the specific variables of the unknown type interface and their types are shown by running the results:


    1. Get interface's reflect first. Type, and then traverse through the Numfield
    2. Again through the reflect. The field of type gets its field
    3. Finally, the corresponding value is obtained through the interface () field.


You can tell by running the result that the method (function) for obtaining an unknown type of interface is:


    1. Get interface's reflect first. Type, and then traverse through the Nummethod
    2. And then separately through the reflect. The method of type gets the corresponding real way (function)
    3. Finally, the name and type of the result are used to know the specific method name
    4. That is, reflection can then re-convert the "Reflection type Object" to "interface type variable"
    5. The nesting of struct or struct is the same as the judgment processing method


Through reflect. Value sets the values of the actual variables



Reflect. Value is obtained through reflect.valueof (x) and can be passed through Reflec only if x is a pointer. Value modifies the values of the actual variable x, namely: To modify the object of the reflection type, it is important to ensure that its value is "addressable".



Examples are as follows:


package main

import (
"fmt"
"reflect"
)

func main () {

var num float64 = 1.2345
fmt.Println ("old value of pointer:", num)

// Get reflect.Value in num via reflect.ValueOf. Note that the parameter must be a pointer to modify its value.
pointer: = reflect.ValueOf (& num)
newValue: = pointer.Elem ()

fmt.Println ("type of pointer:", newValue.Type ())
fmt.Println ("settability of pointer:", newValue.CanSet ())

// reassign
newValue.SetFloat (77)
fmt.Println ("new value of pointer:", num)

//////////////////////
// What happens if the parameter of reflect.ValueOf is not a pointer?
pointer = reflect.ValueOf (num)
// newValue = pointer.Elem () // If it is not a pointer, panic directly here, "panic: reflect: call of reflect.Value.Elem on float64 Value"
}

operation result:
old value of pointer: 1.2345
type of pointer: float64
settability of pointer: true
new value of pointer: 77


Description


    1. The parameter that needs to be passed in is * float64 this pointer, which can then be passed through pointer. Elem () to get the value pointed to, note that it must be a pointer.
    2. If the parameter passed in is not a pointer, but a variable, then
      • The object corresponding to the original value is obtained by Elem directly panic
      • Query whether you can set return False by using the Canset method
    3. Newvalue.cantset () Indicates whether the value can be reset, if the output is true can be modified, otherwise it can not be modified, and then the printing after the discovery has really been modified.
    4. Reflect. Value.elem () means to get the reflected object corresponding to the original value, only the original object can be modified, and the current reflection object cannot be modified.
    5. That is, if you want to modify the reflection type object, its value must be "addressable" corresponding to the pointer to pass in, and to get the original value corresponding to the reflection object through the Elem method "
    6. The nesting of struct or struct is the same as the judgment processing method


Through reflect. ValueOf to make a call to the method



This is a high-level usage, and we're just talking about the use of several reflections on types and variables, including how to get their values, their types, and if they re-set new values. But in engineering applications, another common and advanced usage is the invocation of the method "function" through reflect. For example, when we want to do framework engineering, we need to be able to expand the method arbitrarily, or the user can customize the method, then what means can we extend the user to customize it? The key point is that the user's custom method is unknown, so we can do it through reflect.



Examples are as follows:


package main

import (
"fmt"
"reflect"
)

type User struct {
Id int
Name string
Age int
}

func (u User) ReflectCallFuncHasArgs (name string, age int) {
fmt.Println ("ReflectCallFuncHasArgs name:", name, ", age:", age, "and origal User.Name:", u.Name)
}

func (u User) ReflectCallFuncNoArgs () {
fmt.Println ("ReflectCallFuncNoArgs")
}

// How do I make a method call through reflection?
// could have been called directly with u.ReflectCallFuncXXX, but if you want to pass reflection, you must first register the method, which is MethodByName, and then call mv.Call through reflection

func main () {
user: = User {1, "Allen.Wu", 25}
Ranch
// 1. To call the corresponding method through reflection, you must first obtain reflect.Value through reflect.ValueOf (interface) and get the "reflection type object" before you can do the next step.
getValue: = reflect.ValueOf (user)

// Be sure to specify the parameter as the correct method name
// 2. First look at the calling method with parameters
methodValue: = getValue.MethodByName ("ReflectCallFuncHasArgs")
args: = [] reflect.Value {reflect.ValueOf ("wudebao"), reflect.ValueOf (30)}
methodValue.Call (args)

// Be sure to specify the parameter as the correct method name
// 3. Look at the call method without parameters
methodValue = getValue.MethodByName ("ReflectCallFuncNoArgs")
args = make ([] reflect.Value, 0)
methodValue.Call (args)
}


operation result:
ReflectCallFuncHasArgs name: wudebao, age: 30 and origal User.Name: Allen.Wu
ReflectCallFuncNoArgs


Description


    1. To invoke the corresponding method by reflection, you must first pass the reflect. ValueOf (interface) to get to reflect. Value, the "Reflection type Object" can be obtained before the next processing

    2. Reflect. Value.methodbyname this. Methodbyname, you need to specify the exact true method name, if the error will be directly panic,methodbyname return a function value corresponding to the name of the Reflect.value method.

    3. []reflect. Value, which is the parameter of the method that will eventually need to be called, can be no or one or more, depending on the actual parameters.

    4. Reflect. Call this method of value, this method will eventually invoke the real method, and the parameters must be consistent if reflect. Value ' kind is not a method, then it will be panic directly.

    5. could have used U. Reflectcallfuncxxx is called directly, but if you want to pass reflection, you first register the method, that is, Methodbyname, and then call Methodvalue.call by reflection


Reflection reflect performance of Golang



Golang's reflexes are slow, and this is related to its API design. In Java, we usually use reflection to do this.


Field field = clazz.getField("hello");
field.get(obj1);
field.get(obj2);


The type of reflection object obtained is Java.lang.reflect.Field. It can be reused. As long as the different obj is passed in, the corresponding field on this obj can be obtained.



But Golang's reflections are not designed like this:


type_ := reflect.TypeOf(obj)field, _ := type_.FieldByName("hello")


The Field object to be taken out here is reflect. The Structfield type, but it has no way to get the value on the corresponding object. If you want to take a value, you have to use a different set of reflections on object instead of type.


type_ := reflect.ValueOf(obj)fieldValue := type_.FieldByName("hello")


The Fieldvalue type that is taken out here is reflect. Value, which is a specific value, rather than a reusable reflection object, each reflection requires the reflect of malloc. Value struct, and also involves GC.



Summary



Golang reflect slow mainly has two reasons


    1. Related to memory allocation and subsequent GC;

    2. The reflect implementation has a large number of enumerations, that is, for loops, such as types.


Summarize



The above details the various functions and usages of the reflective reflect of Golang, which are accompanied by corresponding examples, which are believed to be able to be applied in the engineering application and summarize the following:


    • Reflection can greatly increase the flexibility of the program, so that interface{} has more room to play

      • Reflection must be combined with interface to play.
      • The type of the variable if concrete type (that is, the interface variable) is reflected.
    • Reflection can convert an "interface type variable" to a "Reflection type Object"

      • Reflection uses the TypeOf and VALUEOF functions to get target object information from the interface
    • Reflection can convert a reflection type object to an interface type variable

      • Reflect.value.Interface (). (Known types)
      • Traverse reflect. The field of type gets its field
    • Reflection can modify a reflection type object, but its value must be "addressable"

      • You want to use reflection to modify the state of an object, if Interface.data is settable, which is pointer-interface
    • The method can be called "dynamic" by reflection

    • Because Golang itself does not support templates, it is often necessary to use reflection (reflect) in scenarios where templates are used in the past to achieve


Reference links


    • The Go Blog: In fact, look at the official description is enough!

    • Official Reflect-kind

    • The three laws of the reflection of Go language

    • Go Basic Learning Five interface interface, reflection reflection

    • Improve the reflective performance of Golang

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.