preface

Interfaces are an important concept in Go, and Reflect is based on them. This paper is not only a comprehensive review of the Go language interface and Reflect related knowledge, but also a summary of my stage learning, in order to review the past and learn something new. Be prepared for a longer article.

Interface (s)

define

In the Go language, if a custom type (such as a struct) implements all the methods in an interface, then that type can be said to implement the interface. Interfaces can be defined as follows:

typeInterface name Interface {method1(parameter list) Return value list method1(parameter list) Return value list... }Copy the code

An interface is a collection of methods, but you don’t need to implement them, and there are no variables in an interface. The collection of methods in an interface can represent the characteristics and capabilities of an object, and can be implemented as needed when custom types need to use these methods. Here’s an example:

package main

import (
	"fmt"
)

type Animal interface {
    Eat()
    Run()
}

type Dog struct {
    Name string
}

type Cat struct {
    Name string
}

func (dog *Dog) Eat(a) {
    fmt.Printf("%s is eating.", dog.Name)
}

func (dog *Dog) Run(a) {
    fmt.Printf("%s is running.", dog.Name)
}

func (cat *Cat) Eat(a) {
    fmt.Printf("%s is eating.", cat.Name)
}

func (cat *Cat) Run(a) {
    fmt.Printf("%s is running.", cat.Name)
}

func main(a) {
	var animal1 Animal
	animal1 = &Dog{"doggy"}
	animal1.Eat()
	animal1.Run()

	var animal2 Animal
	animal2 = &Cat{"catty"}
	animal2.Eat()
	animal2.Run()
}
Copy the code

This defines an Animal interface, as well as the Dog and Cat types. Both Dog and Cat implement methods in the Animal interface, so Dog and Cat are Animal types. The interface itself cannot be instantiated, but as you can see from the example above, variables of the interface type can point to an instance of a custom type that implements the interface. The interface type defaults to a pointer (reference type) and prints nil if it is used without initializing the interface.

Empty interface

The empty interface interface{} has no methods, so all types implement the empty interface, that is, we can assign any variable to the empty interface. Modify the main function above:

func main(a) {
    var animal interface{}
	dog := &Dog{"doggy"}
	animal = dog
	fmt.Println(animal)
}
Copy the code

Running results:

&{doggy}
Copy the code

Interface inheritance

An interface can inherit from multiple other interfaces, and if you want to implement this interface, you must implement methods in all interfaces that you inherit.

package main

import (
	"fmt"
)

type Eater interface {
	Eat()
}

type Runner interface {
	Run()
}

type Animal interface {
	Eater
	Runner
}

// Define a Dog struct and implement the eat and run methods, thus implementing the animal interface
type Dog struct {
	Name string
}

func (dog *Dog) Eat(a) {
	fmt.Printf("%s is eating.", dog.Name)
}

func (dog *Dog) Run(a) {
	fmt.Printf("%s is running.", dog.Name)
}

func main(a) {
	var animal1 Animal
	animal1 = &Dog{"doggy"}
	animal1.Eat()
	animal1.Run()
}

Copy the code

Types of assertions

When we are not sure what type of variable is stored in an interface variable, we can use type assertions to determine the type of the variable.

var animal1 Animal
animal1 = &Dog{"doggy"}
dog := animal1.(*Dog)
Copy the code

If the type does not match, panic will be reported. Therefore, a detection mechanism should be added. If it succeeds, it will be OK.

var animal1 Animal
animal1 = &Dog{"doggy"}

if dog, ok := animal1.(*Dog); ok {
	fmt.Println("convert success")
    dog.Run()
} else {
	fmt.Println("convert fail")}Copy the code

Alternatively, we can use switch-type syntax for type assertion:

package main

import (
	"fmt"
)

type Eater interface {
	Eat()
}

type Runner interface {
	Run()
}

type Animal interface {
	Eater
	Runner
}

type Dog struct {
	Name string
}

type Cat struct {
	Name string
}

func (dog *Dog) Eat(a) {
	fmt.Printf("%s is eating.", dog.Name)
}

func (dog *Dog) Run(a) {
	fmt.Printf("%s is running.", dog.Name)
}

func (cat *Cat) Eat(a) {
	fmt.Printf("%s is eating.", cat.Name)
}

func (cat *Cat) Run(a) {
	fmt.Printf("%s is running.", cat.Name)
}

func TypeJudge(animals ...interface{}) {
	for index, animal := range animals {
		switch animal.(type) {
		case *Dog:
			fmt.Printf("The %d argument is of type Dog \n", index)
		case *Cat:
			fmt.Printf("The %d argument is Cat type \n", index)
		default:
			fmt.Println("Indeterminate type")}}}func main(a) {
	var animal1 Animal
	animal1 = &Dog{"doggy"}

	var animal2 Animal
	animal2 = &Cat{"catty"}

	TypeJudge(animal1, animal2)
}
Copy the code

role

Interface is useful for the Go language because it enables generics, such as a function that needs to accept different types of arguments or return different types of values, rather than specifying the types of arguments or return values at the beginning. This allows functions to support all types:

func FuncName(arg1 interface{}, rest ...interface{}) interface{} {
    // ...
}
Copy the code

Object-oriented languages such as C++ and Java all have the feature of polymorphism. Interface can be said to be a form to realize polymorphism in Go language. The same interface can be implemented by different classes (custom types), which can call functions with the same function name but implement completely different functions.

Sometimes we can use interfaces to do very clever things: we usually specify a specific type when defining a slice, but sometimes we need the elements in the slice to be variables of any type, and this is where interface comes in handy. The following is the go code to update the data in the database table, using interface to achieve the operation, readers can experience the convenience brought by interface:

func generateSQLForUpdatingArticle(article model.ArticleStruct) (stringAnd []interface{}) {
	var columns = make([]string.0)
	var arguments = make([]interface{}, 0)

	if len(article.CommentCount) > 0 {
		columns = append(columns, "comment_count = ?")
		arguments = append(arguments, article.CommentCount)
	}

	if len(article.Source) > 0 {
		columns = append(columns, "source = ?")
		arguments = append(arguments, article.Source)
	}

	if len(article.Summary) > 0 {
		columns = append(columns, "summary = ?")
		arguments = append(arguments, article.Summary)
	}

	if len(article.Content) > 0 {
		columns = append(columns, "content = ?")
		arguments = append(arguments, article.Content)
	}

	sql := fmt.Sprintf("UPDATE article_structs SET %s WHERE sid = %s", strings.Join(columns, ","), article.Sid)
	return sql, arguments
}

func UpdateArticle(article model.ArticleStruct) error {
	sql, arguments := generateSQLForUpdatingArticle(article)
	iferr := db.Exec(sql, arguments...) .Error; err ! =nil {
		log.Println("Updating article failed with error:", err)
		return err
	}
	return nil
}
Copy the code

However, empty interface interface {} although can save any value, but also brings a problem: an empty interface will hide value corresponding to the presentation and all public methods, so only we know the specific type of dynamic type to use assertions to access to the internal value, for the internal value and no special things can be done; If we don’t know in advance what type of value the empty interface points to, we may be at a loss.

If you want to know what the variable of an interface type is and what its capabilities are, you need a “mirror” to reflect the specific content of the variable. Reflect also happens to have a tool like this in Go.

Reflect (reflection)

concept

In computer science, reflection refers to a class of applications that are self-describing and self-controlling. In other words, this kind of application adopts some mechanism to realize self-representation and 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 own behavior.

Languages that support reflection can integrate reflection information about variables, such as field names, type information, and structure information, into executable files at compile time, and provide programs with interfaces to access reflection information so that they can obtain reflection information about types at run time and have the ability to modify them.

Before we get to reflection, we need to understand some of Golang’s principles for type design:

A variable has two parts: type and value.

Type is divided into static type and concrete type. Static type (int, string, bool, etc.); Concrete Type is the type that the Runtime system sees.

The success of a variable of an interface type in type assertion depends on concrete Type rather than static Type.

The type of a variable specified in Go is static, which is determined when the variable is created. Reflection is mainly used with interface type variables, all of which are concrete types.

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

(value, type)
Copy the code

The interface variable contains two Pointers to the value and type of the actual variable (corresponding to Concrete Type). The existence of interface and its pair is a prerequisite for Golang to implement reflection, which is the mechanism used to detect the values and types stored inside the interface type variables. That brings us to the two data classes Type and Value in the Reflect package.

Reflect. The Type and reflect the Value

reflect.Type

The reflect package defines the Type interface as follows:

type Type interface {
    // Kind returns the type of the interface
    Kind() Kind
    // Name returns the Name of the type in its own package, or "" if it is an unnamed type.
    Name() string
    // PkgPath returns the package path of type, that is, explicitly specify the package import path, such as "encoding/base64"
    // If the type is built in (string, error) or unnamed (*T, struct{}, []int), "" is returned
    PkgPath() string
    // Returns a string representation of the type. The string may use a short package name (e.g., base64 instead of "encoding/base64")
    // There is no guarantee that each type of string represents something different. If you want to compare two types for equality, use the Type Type directly.
    String() string
    // Returns the number of bytes required to store a value of that type; Similar to the unsafe. Sizeof
    Size() uintptr
    // Returns the number of bytes that will be aligned when a value of this type is applied from memory
    Align() int
    // Returns the number of bytes aligned when the type is a field in a structure
    FieldAlign() int
    // If this type implements the interface represented by u, it returns true
    Implements(u Type) bool
    Return true if the value of this type can be assigned directly to the type represented by u
    AssignableTo(u Type) bool
    Return true if the value of this type can be converted to the type represented by u
    ConvertibleTo(u Type) bool
    // Returns the number of bytes of the type. If the Kind of the type is not an Int, Uint, Float, or Complex, it will panic
    Bits() int
    // Return the length of the array type. If the array type is not panic
    Len() int
    // Returns the element type of the type, and panic occurs if Kind is not Array, Chan, Map, Ptr, or Slice
    Elem() Type
    // Returns the type of the key of type map. Non-mapped types will panic
    Key() Type
    // Return the direction of a channel type. If not, panic will occur
    ChanDir() ChanDir

    // Return the number of fields of the struct type (anonymous fields count as one field), such as the non-structure type panic
    NumField() int
    // Return the type of the i-th field of the struct type, such as non-structure or I not in [0, NumField())) will panic
    Field(i int) StructField
    // Return the type of the nested field specified by the index sequence,
    // This is equivalent to calling this method chained with each value in the index
    FieldByIndex(index []int) StructField
    // Returns a field of the type named name (looking for anonymous fields and their subfields),
    // The Boolean value indicates whether a non-structure panic is found
    FieldByName(name string) (StructField, bool)
    // Return the first field of the type whose name matches that of the function match, Boolean if found, if not, panic will occur
    FieldByNameFunc(match func(string) bool) (StructField, bool)// If the last input parameter of the function type is "..." Parameter of form,IsVariadicReturn true if so,t.In(t.NumIn()- 1) Returns the implicit actual type of the argument (a slice of the declared type) // if the non-function type willpanic
    IsVariadic(a) bool/ / returnfuncThe number of arguments to a type, if not a function, willpanic
    NumIn(a) int/ / returnfuncThe type of the firstiThe type of an argument, such as a non-function oriNot in [0,NumIn(a)Will be within)panic
    In(i int) Type/ / returnfuncThe number of values returned by a type, if not a function, willpanic
    NumOut(a) int/ / returnfuncThe type of the firstiThe type of a return value, such as a non-function oriNot in [0,NumOut(a)Will be within)panic
    Out(i int) Type// Returns the number of methods in the method set of this type // anonymous field methods are counted; Methods of the body type mask methods of the same name for anonymous fields; // Ambiguities caused by anonymous fields are filtered outNumMethod(a) int// Returns the first in the method set of this typeiA method,iNot in [0,NumMethod(a)) will result inpanic// Pair non-interface typesTOr *T, return the valueTypeFields andFuncThe field describes the method's unbound function status // for the interface type, return the value ofTypeThe field describes the signature of the method,FuncField isnil
    Method(int) Method// Returns a method in the method set of that type based on the method name, using a Boolean value indicating whether the method was found // pairs non-interface typesTOr *T, return the valueTypeFields andFuncThe field describes the method's unbound function status // for the interface type, return the value ofTypeThe field describes the signature of the method,FuncField isnil
    MethodByName(string) (Method, bool)// contains hidden or non-exported methods}Copy the code

TypeOf accepts any interface{} Type and returns the corresponding dynamic Type reflect.type:

num := reflect.TypeOf(1)
fmt.Println(num.String())
fmt.Println(num)
Copy the code

Take a look at the TypeOf() implementation:

// 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 {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}
Copy the code

The TypeOf function takes an interface{} as an argument, and inside the function an implicit conversion is performed to convert the value 1 to a variable of empty interface type. This variable contains two parts of information: 1 the variable’s dynamic type (int) and dynamic value (1); Finally, the return value of the TypeOf is of Type reflect.Type (we call it a reflection Type object), which makes it possible to call the methods of the Type interface above to get the required variable information.

  • When the reflected object is of a primitive data type:
func main(a) {
    var s string
    rString := reflect.TypeOf(s)
    fmt.Println(rString)         //string
    fmt.Println(rString.Name())  //string returns a string representing the type name
    fmt.Println(rString.Kind())  //string returns a constant of type reflect.Kind
}
Copy the code
  • When the reflected object is of type pointer:
type Dog struct {
	Name string
	Age  int
}

func main(a) {
    dogPtr := &Dog{"doggy"}
    rDogPtr := reflect.TypeOf(dogPtr)
    
    fmt.Println(rDogPtr.Name())  / / is empty
    fmt.Println(rDogPtr.Kind())  // ptr
    
    // Elem() can get the actual variable to which the pointer points
    rDog := rDogPtr.Elem()
    fmt.Println(rDogPtr.Name())  // Dog
    fmt.Println(rDogPtr.Kind())  // struct
}
Copy the code

You can see that when you get a reflection object from a pointer, you can’t use Name() and Kind() directly, so you can only get information about the pointer. You can use Elem() to get the actual variable to which the pointer points.

  • When the reflected object is of structure type:

If the reflection object’s type is a structure, the NumField() and Field() methods can be used to obtain details about the members of the structure.

type Dog struct {
	Name string
	Age  int
}

func main(a) {
	dog := Dog{"doggy".2}
	rDog := reflect.TypeOf(dog)

	fmt.Printf("%v ", rDog.Name()) // Dog
	fmt.Println(rDog.Kind())       // struct

	for index := 0; index < rDog.NumField(); index++ {
		fmt.Printf("%v ", rDog.Field(index).Name)
		fmt.Println(rDog.Field(index).Type)
	}
}
Copy the code

Run output:

Dog struct
Name string
Age int
Copy the code
reflect.Value

The reflect package defines the Value type as follows:

type Value struct {
    // typ holds the type of the value represented by a Value.
	typ *rtype

	// Pointer-valued data or, if flagIndir is set, pointer to data.
	// Valid when either flagIndir is set or typ.pointers() is true.
	ptr unsafe.Pointer
	
	// flag holds metadata about the value.
	flag
}
Copy the code

You can see that the Value type contains a type pointer, a Value pointer, and flag information. There are also a number of methods for the Value type, including the method used to get the Value:

func (v Value) Int(a) int64/ / to getintType value, ifvIf the value is not a signed integer, thenpanic.func (v Value) Uint(a) uint64/ / to getunitType value ifvValue is not an unsigned integeruintptr),panic.func (v Value) Float(a) float64/ / to getfloatType value ifvThe value is not a floating point, thenpanic.func (v Value) Complex(a) complex128// Get the value of the complex type ifvIf the value is not complex, thenpanic.func (v Value) Bool(a) bool// Get a Boolean value ifvIf the value is not a Boolean, thenpanic.func (v Value) Len(a) int/ / to getvThe length of the value,vValues must be strings, arrays, slices, maps, channels.func (v Value) Cap(a) int/ / to getvThe capacity of value,vValues must be numeric, slice, channel.func (v Value) Index(i int) reflect.Value/ / to getvValue of the firstiAn element,vValues must be strings, arrays, slices,iCan't go out of range.func (v Value) Bytes(a) []byte// Get the value of the byte type ifvThe value is not a byte slice, thenpanic.func (v Value) Slice(i, j int) reflect.Value/ / to getvValue of slice, slice length =j - i, slice capacity =v.Cap(a) - i. //vMust be a string, number, slice, or addressable if an array.iCan't go out of range.func (v Value) Slice3(i, j, k int) reflect.Value/ / to getvValue of slice, slice length =j - i, slice capacity =k - i. //i,j,kNot beyondvThe capacity.i< =j< =k. //vMust be a string, number, slice, or addressable if an array.iCan't go out of range.func (v Value) MapIndex(key Value) reflect.Value/ / according tokeyThe key to obtainvThe contents of the value,vValues must be mappings. // If the specified element does not exist, orvValue is an uninitialized map, returns zero (reflect.ValueOf(nil))func (v Value) MapKeys(a) []reflect.Value/ / to getvAn unordered list of all the keys of the value,vValues must be mappings. / / ifvValue is an uninitialized mapping, returns an empty list.func (v Value) OverflowInt(x int64) bool/ / determinexWhether the beyondvThe range of values,vThe value must be a signed integer.func (v Value) OverflowUint(x uint64) bool/ / determinexWhether the beyondvThe range of values,vThe value must be an unsigned integer.func (v Value) OverflowFloat(x float64) bool/ / determinexWhether the beyondvThe range of values,vThe value must be floating point.func (v Value) OverflowComplex(x complex128) bool/ / determinexWhether the beyondvThe range of values,vThe value must be complex.Copy the code

Methods used to set values:

func (v Value) SetUint(x uint64)// Sets the value of an unsigned integerfunc (v Value) SetFloat(x float64)// Sets a floating point valuefunc (v Value) SetComplex(x complex128)// Set the complex type valuefunc (v Value) SetBool(x bool)// Sets a Boolean valuefunc (v Value) SetString(x string)// Sets a string valuefunc (v Value) SetLen(n int)// Set the length of the slice,nIt can't go out of range. It can't be negative.func (v Value) SetCap(n int)// Set the size of the slicefunc (v Value) SetBytes(x []byte)// Sets the value of the byte typefunc (v Value) SetMapIndex(key, val reflect.Value)/ / setmapthekeyandvalue, the premise must be after initialization, there is no overwrite, no addfunc (v Value) Set(x Value)/ / will bevChange the holding value ofxThe holding value of. ifv.CanSet(a)Return false, yespanic.xThe holding value of must be directly assigned tovThe type that holds the value.Copy the code

Other methods:

Structural correlation:func (v Value) NumField(a) int// Get the number of structure fields (members)func (v Value) Field(i int) reflect.Value// Get the struct field by indexfunc (v Value) FieldByIndex(index []int) reflect.Value// Get the structure nested fields according to the index chainfunc (v Value) FieldByName(string) reflect.Value// Get struct field by name, no returnreflect.ValueOf(nil)

func (v Value) FieldByNameFunc(match func(string) bool) Value// According to the matching functionmatchGets the field, or returns zero if there is no matching field (reflect.ValueOf(nil)) Channel correlation:func (v Value) Send(x reflect.Value)// Send data (blocks),vValues must be writable channels.func (v Value) Recv(a) (x reflect.Value, ok bool)// Receive data (block),vValues must be readable channels.func (v Value) TrySend(x reflect.Value) bool// Try to send data (without blocking),vValues must be writable channels.func (v Value) TryRecv(a) (x reflect.Value, ok bool)// Try to receive data (without blocking),vValues must be readable channels.func (v Value) Close(a)// Turn off channel function correlationfunc (v Value) Call(in []Value) (r []Value)// Pass the parameter listincallvThe function (or method) represented by the value. The return value of the function is storedrIn the back. // How many arguments to pass ininHow many elements are stored in. //CallYou can call either a fixed-parameter function (with a fixed number of arguments) or a variable parameter function (with a variable number of arguments).func (v Value) CallSlice(in []Value) []Value// Call the argument functionCopy the code

Similarly, we can take any interface{} type through reflect.valueof and return the corresponding dynamic type reflect.value:

v := reflect.ValueOf(2)
fmt.Println(v)  / / 2
fmt.Println(v.String()) // <int Value>
Copy the code

Take a look at the reflect.valueof implementation code:

func ValueOf(i interface{}) Value {
	if i == nil {
		return Value{}
	}

	// TODO: Maybe allow contents of a Value to live on the stack.
	// For now we make the contents always escape to the heap. It
	// makes life easier in a few places (see chanrecv/mapassign
	// comment below).
	escapes(i)

	return unpackEface(i)
}

// unpackEface converts the empty interface i to a Value.
func unpackEface(i interface{}) Value {
	e := (*emptyInterface)(unsafe.Pointer(&i))
	// NOTE: don't read e.word until we know whether it is really a pointer or not.
	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

For escapes(), which deals with stack and heap object allocation and escape analysis, read William Kennedy’s series escape Analysis of the Go language mechanism

Like reflect.TypeOf, ValueOf takes an interface{} as an argument, and inside the function an implicit conversion is performed to the input parameter, converting it to an empty interface type variable, and eventually returning a Value object. The reflect.valueof return value is also a reflection object.

Note that the Value object also contains the Type information of the actual Value. The Type() method of Value returns the reflect.type corresponding to the specific Type:

v := reflect.ValueOf(2)
t := v.Type()
fmt.Println(t) // int
fmt.Println(t.String()) // int
Copy the code

throughrelfect.ValueGet information about the actual variables

ValueOf = reflect.valueof = reflect.valueof = reflect.valueof = reflect.valueof = reflect.valueof = reflect.valueof = reflect.valueof

v := reflect.ValueOf(2)
i := v.Interface()
if num, ok := i.(int); ok { // Type assertion
	fmt.Println(num)
}
Copy the code

However, in real scenarios, we do not know the Type of the original Value, so we need to use the reflect.Type and reflect.Value methods to explore the information of the original Value. Here is an example:

package main

import (
	"fmt"
	"reflect"
)

type Dog struct {
	Name string
	Age  int
}

func (dog *Dog) Eat(a) {
	fmt.Printf("%s is eating.", dog.Name)
}

func (dog *Dog) Run(a) {
	fmt.Printf("%s is running.", dog.Name)
}

func (dog Dog) Sleep(a) {
	fmt.Printf("%s is sleeping.", dog.Name)
}

func (dog Dog) Jump(a) {
	fmt.Printf("%s is jumping.", dog.Name)
}

func main(a) {
	doggy := Dog{"doggy".2}
	checkFieldAndMethod(doggy)

	fmt.Println("")
	tommy := &Dog{"tommy".2}
	checkFieldAndMethod(tommy)
}

func checkFieldAndMethod(input interface{}) {
	inputType := reflect.TypeOf(input)
	fmt.Println("Type of input is :", inputType.Name())
	inputValue := reflect.ValueOf(input)
	fmt.Println("Value of input is :", inputValue)

	// If input is a pointer to a primitive type, use the Elem() method or Indirect() to obtain the value to which the pointer points
	if inputValue.Kind() == reflect.Ptr {
		inputValue = inputValue.Elem()
		// inputValue = reflect.Indirect(inputValue)
		fmt.Println("Value input points to is :", inputValue)
	}

	// Use NumField() to get the number of fields in the structure, and iterate to get the value Field(I) and Type Field(I).type ().
	for i := 0; i < inputValue.NumField(); i++ {
		field := inputValue.Type().Field(i)
		value := inputValue.Field(i).Interface()
		fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
	}

	// Get method
	for i := 0; i < inputType.NumMethod(); i++ {
		m := inputType.Method(i)
		fmt.Printf("%s: %v\n", m.Name, m.Type)
	}
}
Copy the code

Output after run:

Type of input is : Dog
Value of input is : {doggy 2}
Name: string = doggy
Age: int = 2
Jump: func(main.Dog)
Sleep: func(main.Dog)

Type of input is : 
Value of input is : &{tommy 2}
Value input points to is : {tommy 2}
Name: string = tommy
Age: int = 2
Eat: func(*main.Dog)
Jump: func(*main.Dog)
Run: func(*main.Dog)
Sleep: func(*main.Dog)
Copy the code

The steps to get the original worth types and methods using reflection are as follows:

  • Check whether the original value is a value variable or a pointer variable. If it is a pointer variable, passElem()Methods orIndirect()Gets the value to which the pointer points;
  • useNumField()Gets the number of fields in the structure, traversed to get the value of the fieldField(i)And typeField(i).Type();
  • useNumMethod()Get the structure of the method, traversal to get the name and type of the method.

In addition, when using the reflect.value procedure, I am sometimes confused about the difference between the Elem() method and the Indirect() method.

// Elem returns the value that the interface v contains
// or that the pointer v points to.
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
func (v Value) Elem(a) Value

// Indirect returns the value that v points to. / /If v is a nil pointer.Indirect returns a zero Value. / /If v is not a pointer.Indirect returns v.
func Indirect(v Value) Value
Copy the code
  • ElemReturns the value of the interface held by vValueEncapsulate, or v holds, the value to which the pointer pointsValueEncapsulation. If vKindnotInterfaceorPtrwillpanic; If v holds a value ofnilThat will be returnedValueZero value.
  • IndirectReturns the value to which a pointer to v is heldValueEncapsulation. If v holds a value ofnilThat will be returnedValueZero value. If v holds a variable that is not a pointer, the original value v is returned.

That is, the Elem() method and Indirect() are equivalent when v holds a variable that is a pointer.

The observant reader may notice some differences in the methods used to fetch variables by reflection between value variables and pointer variables, but I’ll leave that to the reader.

throughrelfect.ValueModify information about the actual variable

When modifying information about the actual variable via relect.Value is commonly used with the following reflected Value objects:

func (v Value) Elem(a) Value  
//Elem(a)returnvThe value held by the interfaceValueEncapsulate, orvHolds the value to which the pointer pointsValueEncapsulation, similar to the * operation, at this timeValueRepresents theValueElement and addressable.func (v Value) Addr(a) Value 
//Addr(a)Returns a holding pointervPointer to the address of the variableValueEncapsulation, similar to the & operation.func (v Value) CanAddr(a) bool
//CanAddr(a)Returns whether it is retrievablevHolds a pointer to a value. Values that can get Pointers are said to be addressable.func (v Value) CanSet(a) bool
//CanSet(a)returnvWhether the held value can be modifiedCopy the code

However, it’s worth noting that not all reflects. Value values can be modified. Consider the following example:

package main 

import(
    "fmt"
    "reflect"
)

func main(a) {
    a := 1
	rA := reflect.ValueOf(a)
	fmt.Println(rA.CanSet()) //false

	rAptr := reflect.ValueOf(&a)
	rA2 := rAptr.Elem()
	fmt.Println(rA2.CanSet()) //true
	rA2.SetInt(2)
	fmt.Println(rA2.Int()) / / 2
}
Copy the code

There are two conditions for modifying the value of a reflection type variable:

  • The value of the reflection type variable isaddressableOf, that is, to take the address;
  • The value of the reflection type variable comes from the export field.

Some modified reflection type variables are addressable, and some are not:

package main

import (
    "reflect"
    "fmt"
)

func main(a) {
    x := 2
    a := reflect.ValueOf(2)
    b := reflect.ValueOf(x)
    c := reflect.ValueOf(&x)
    d := c.Elem()
    fmt.Println(a.CanAddr()) // false
    fmt.Println(b.CanAddr()) // false
    fmt.Println(c.CanAddr()) // false
    fmt.Println(d.CanAddr()) // true

}
Copy the code

For a non-pointer variable x, the reflect.value returned by reflect.valueof (x) does not take the address. But for D, it is generated by the dereference of C, pointing to another variable, and therefore addressable. We can get the reflected ValueOf x’s desirable address by calling reflect.valueof (&x).elem ().

For struct type variables, if the member field is not exported, then it can be accessed but cannot be modified by reflection:

package main

import (
	"fmt"
	"reflect"
)

type Dog struct {
	Name string
	Age  int
	sex  string
}

func main(a) {
    rDog := reflect.ValueOf(&Dog{}).Elem()
	vAge := rDog.FieldByName("Age")
	vAge.SetInt(1)

	vSex := rDog.FieldByName("sex")
	vSex.SetString("male")}Copy the code

Error: SetString uses a value from an unexported field.

panic: reflect: reflect.Value.SetString using value obtained using unexported field
Copy the code

To be able to change this value, you need to export the field. Capitalize the sex member of the Dog type.

The Value of the variable held by reflect.Value of the desirable address can be modified by getting a pointer to the actual Value from the reflected type variable, in addition to the reflected Set series of methods:

package main

import (
    "reflect"
    "fmt"
)

func main(a) {
    x := 1
    v := reflect.ValueOf(&x).Elem()
    px := v.Addr().Interface().(*int)
    *px = 2
    fmt.Print(x) / / 2
}
Copy the code

The Addr() method is called to return a Value that holds a pointer to the variable; The Interface() method is then called on the Value, returning an Interface {} containing a pointer to the variable; Finally, the value of the variable is modified by obtaining a plain pointer through a type assertion.

Call a function through reflection

If reflect.Value holds a Value of type function, the function can be called through reflect.Value.

func (v Value) Call(in []Value) []Value
Copy the code

The Call method calls the function held by V with the input argument in. The in argument is a slice of the reflected Value object, i.e. [] reflect.value; When the call completes, the return Value of the function is returned via [] reflect.value.

package main 

import(
    "fmt"
    "reflect"
)
func add(a, b int) int {

    return a + b
}

func main(a) {

    // Wrap the function add as a reflection value object
    funcValue := reflect.ValueOf(add)

    // Constructor add takes two integer values
    paramList := []reflect.Value{reflect.ValueOf(5), reflect.ValueOf(10)}

    // reflection Call()
    retList := funcValue.Call(paramList)

    // Get the first return value, integer value
    fmt.Println(retList[0].Int()) / / return to 15
}
Copy the code

If you need to call a structure’s methods via reflection, you can use the MethodByName method to do so:

func (v Value) MethodByName(name string) Value/ / returnvcallednameMethod has been bound tovOf the state that holds a valueValueEncapsulation.Copy the code

For example:

package main 

import(
    "fmt"
    "reflect"
)

type Dog struct {
	Name string
	Age  int
}

func (dog *Dog) SetName(name string){
    dog.Name = name
}

func main(a) {
    dog := Dog{}
	rDog := reflect.ValueOf(&dog)
	paramList1 := []reflect.Value{reflect.ValueOf("doggy")}
	rDog.MethodByName("SetName").Call(paramList1)
	fmt.Println(dog.Name) //doggy
}
Copy the code

It is worth noting that the process of calling a function by reflection requires the construction of a large number of reflects. values and intermediate variables, the checking of function parameter values one by one, and the copying of the call parameters into the parameter memory of the calling function. After the call, the return Value needs to be converted to reflect.Value, and the user needs to fetch the call Value from it. Therefore, the performance problem of reflection calling function is particularly prominent, and the use of reflection calling function is not recommended.

conclusion

In this article, we introduce Reflect through the definition, usage and side effects of interface in Go. We introduce the concept of Reflect in detail through a number of examples, how to get a value through Reflect, how to modify a value, and how to call a function. The content can be said to be quite detailed and concrete, in this process also let the author of this part of the knowledge has a deeper understanding, but also hope to bring readers a little help.

The resources

Golang Standard Library

In-depth understanding and examples from Golang’s Reflections

【Go Addressable 】