当前位置:网站首页>go reflect

go reflect

2022-04-23 20:43:00 baboon_ chen

Reflection

Go Language provides a mechanism , Without knowing the type at compile time , Updatable variables 、 View values at runtime 、 Call methods of type , And directly operate on their layout , This mechanism is called reflection (reflection).

One 、relect.Type

1、 What is? relect.Type?

Go Any variable in the language contains reflect.Type And reflect.Value Two parts of information .

reflect.Type Is when declaring variables type , It's in Go Is defined 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	// The value of the element that the value points to , Similar to the language level * operation . Downtime occurs when the value type is not a pointer or interface , Null pointer returns nil Of Value
    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
}

2、 How to distinguish reflect.Type ( type ) And reflect.Kind( species )?

reflect.Type.Name() Is the type specified when declaring the variable .Go Language is a statically typed language , Each variable has a static type , The type is determined when compiling . No matter how the value of the variable changes , The type of variable never changes . Between two different static types , It cannot be assigned ( If there is no type conversion ).

type MyInt int
var i int = 5
var j MyInt = 10
j = i // build error: cannot use i (type int) as type MyInt in assignment

reflect.Kind yes Go Built in types defined in ( The underlying data type ), Such as these :

type Kind uint
const (
    Invalid Kind = iota  //  Illegal type 
    Bool                 //  Boolean type 
    Int                  //  signed int 
    Int8                 //  A signed 8 An integer 
    Int16                //  A signed 16 An integer 
    Int32                //  A signed 32 An integer 
    Int64                //  A signed 64 An integer 
    Uint                 //  Unsigned integer 
    Uint8                //  Unsigned 8 An integer 
    Uint16               //  Unsigned 16 An integer 
    Uint32               //  Unsigned 32 An integer 
    Uint64               //  Unsigned 64 An integer 
    Uintptr              //  The pointer 
    Float32              //  Single-precision floating-point 
    Float64              //  Double precision floating point 
    Complex64            // 64 Bit plural type 
    Complex128           // 128 Bit plural type 
    Array                //  Array 
    Chan                 //  passageway 
    Func                 //  function 
    Interface            //  Interface 
    Map                  //  mapping 
    Ptr                  //  The pointer 
    Slice                //  section 
    String               //  character string 
    Struct               //  Structure 
    UnsafePointer        //  Bottom pointer 
)

Example : distinguish Type And Kind

package main

import (
	"fmt"
	"reflect"
)

type MyType int

type Stu struct {
    
	name string
	score float32
}

func test1(a interface{
    }) {
    
	fmt.Printf("the type of a is: %v\n", reflect.TypeOf(a))
	fmt.Printf("the kind of a is: %v\n\n", reflect.ValueOf(a).Kind())
}

func main() {
    
	var a Stu
	var b MyType
	test1(a)
	test1(b)
}

Output :

the type of a is: main.Stu
the kind of a is: struct

the type of a is: main.MyType
the kind of a is: int

3、 Pointer reflection

Go When getting a reflection object from a pointer in a language program , Can pass reflect.Elem() Method to get the type of element this pointer points to , This fetch process is called fetch element , It's equivalent to making a pointer type variable * operation .

reflect.Elem() The return is Type

// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type

Go Of all pointer variables in the reflection of language species All are Ptr, But it should be noted that , Pointer variable's Type.Name() yes nil.


Example :

func main() {
    
	type cat struct {
    
	}

	p := &cat{
    }
	typeOfCat := reflect.TypeOf(p)
    fmt.Printf("type: %v\n", typeOfCat)	// reflect.Type yes interface type , So it can be passed to fmtr Print 
	fmt.Printf("TypeName: %v, Kind: %v\n", typeOfCat.Name(), typeOfCat.Kind())
	fmt.Printf("TypeName: %v, Kind: %v\n", typeOfCat.Elem().Name(), typeOfCat.Elem().Kind())
}

Output :

type: *main.cat
TypeName: , Kind: ptr
TypeName: cat, Kind: struct

4、 Structural reflection

If the reflection object is a structure , Then there are the following methods to obtain the member information in the structure :

Method explain
Field(i int) StructField Return the information of the structure field corresponding to the index according to the index , Downtime occurs when the value is not a struct or index out of bounds
NumField() int Returns the number of struct member fields , Downtime occurs when the type is not a struct or index out of bounds
FieldByName(name string) (StructField, bool) Returns the information of the structure field corresponding to the string according to the given string , When not found bool return false, Downtime occurs when the type is not a struct or index out of bounds
FieldByIndex(index []int) StructField When multiple members visit , according to []int The field index of each structure provided , Return the information of the field , Return zero when not found . Downtime occurs when the type is not a struct or index out of bounds
FieldByNameFunc(match func(string) bool) (StructField,bool) Match the required fields according to the match function , Downtime occurs when the value is not a struct or index out of bounds

1)StructField ( Structure field )

reflect.Type Of Field() Method returns StructField structure , This structure describes the member information of the structure , Through this information, we can get the relationship between members and the structure , Such as deviation 、 Indexes 、 Is it an anonymous field 、 Structural tag StructTag etc. , And it can also be passed StructField Of Type Field Further access to Type information of structure members .

StructField The structure is as follows :

type StructField struct {
    
    Name string          //  Field name 
    PkgPath string       //  Field path 
    Type      Type       //  Field reflection type object 
    Tag       StructTag  //  The structure label of the field 
    Offset    uintptr    //  The relative offset of the field in the structure 
    Index     []int      // Type.FieldByIndex Index value returned in 
    Anonymous bool       //  Is it an anonymous field 
}

Example :

func main() {
    
	type cat struct {
    
		Name string
		Color string
		Age int
		Home struct {
    
			Adderess string
			PostCode int
		}
	}

	ins := cat{
    
		Name: "Tom",
		Color: "Dark",
		Age: 5,
		Home: struct{
    
			Adderess string
			PostCode int
		}{
    
			"England",
			1024,
		},
	}

	sType := reflect.TypeOf(ins)

	for i:=0; i < sType.NumField(); i++ {
    
		filed := sType.Field(i)
		fmt.Printf("filedName: %s, Type: %v\n", filed.Name, filed.Type)
	}

	if addrType, ok := sType.FieldByName("Adderess"); ok {
    
		fmt.Printf("Name: %s, Type: %v", addrType.Name, addrType.Type)
	}
}

Output :

filedName: Name, Type: string
filedName: Color, Type: string
filedName: Age, Type: int
// Adderess Belong to Home This member 
filedName: Home, Type: struct {
     Adderess string; PostCode int }

2)StructTag ( Structural tag )

Structure member information reflect.StructField The structure of the Tag It is called structure label StructTag. Structure labels are additional information labels for structure fields .

type StructTag string

Format of structure label

`key1:"value1" key2:"value2"`

The structure tag consists of one or more key value pairs ; Keys and values are separated by colons , There must be no spaces after the colon , Value in double quotes ; Use a space between key value pairs .


Get the value from the structure tag

StructTag Have some methods , Can be done Tag Analysis and extraction of information , As shown below :

func (tag StructTag) Get(key string) string // according to  Tag  The key in gets the corresponding value .
func (tag StructTag) Lookup(key string) (value string, ok bool) // according to  Tag  The key , Whether the query value exists .

Example :

func test4() {
    
	type cat struct {
    
		Name string `json:"name" bson:"NAME"`
		Color string
	}

	ins := cat{
    
		Name: "Tom",
		Color: "Dark",
	}

	sType := reflect.TypeOf(ins)

	if filedName, ok := sType.FieldByName("Name"); ok {
    
		nameJson := filedName.Tag.Get("json")
		fmt.Printf("the json tag of Name is: %s\n", nameJson)
	}
}

Output :

the json tag of Name is: name



Two 、relect.Value


reflect.Value

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

//  Method :
pointer() unsafe.Pointer
Addr() Value	//  Returns the address of an addressable value , Similar to the language level & operation . Downtime when value is not addressable 
Bool() bool		//  It will be worth  bool  Type return 
Bytes() []byte	//  Array values in bytes  []bytes  Type return 
runes() []rune
CanAddr() bool	//  Indicates whether the value is addressable 
CanSet() bool	//  Can the return value be modified . The value is required to be addressable and exported 
Call(in []Value) []Value
CallSlice(in []Value) []Value
call(op string, in []Value) []Value
Cap() int
Close()
Complex() complex128
Elem() Value	//  The value of the element that the value points to , Similar to the language level * operation . Downtime occurs when the value type is not a pointer or interface , Null pointer returns  nil  Of  Value
Field(i int) Value
FieldByIndex(index []int) Value
FieldByName(name string) Value
FieldByNameFunc(match func(string) bool) Value
Float() float64		//  Put the value in double precision (float64) Type return , All floats (float32、float64) All can return to 
Index(i int) Value
Int() int64		//  It will be worth  int64  Type return , All signed integers can return , Type conversion may be required 
CanInterface() bool
Interface() (i interface{
    })	//  It will be worth  interface{}  Type return , You can convert to the specified type by type assertion 
InterfaceData() [2]uintptr
IsNil() bool	//  Whether the return value is  nil. If the value type is not a channel (channel)、 function 、 Interface 、map、 A pointer or   When slicing  panic, Similar to the language level v== nil operation 
IsValid() bool	//  Whether the judgment value is valid .  When the value itself is illegal , return  false, for example  reflect Value Does not contain any value , The value is  nil  etc. .
IsZero() bool
Kind() Kind
Len() int
MapIndex(key Value) Value
MapKeys() []Value
MapRange() *MapIter
Method(i int) Value
NumMethod() int
MethodByName(name string) Value
NumField() int
OverflowComplex(x complex128) bool
OverflowFloat(x float64) bool
OverflowInt(x int64) bool
OverflowUint(x uint64) bool
Pointer() uintptr
Recv() (x Value, ok bool)
recv(nb bool) (val Value, ok bool)
Send(x Value)
send(x Value, nb bool) (selected bool)
Set(x Value)
SetBool(x bool)
SetBytes(x []byte)
setRunes(x []rune)
SetComplex(x complex128)
SetFloat(x float64)
SetInt(x int64)
SetLen(n int)
SetCap(n int)
SetMapIndex(key Value, elem Value)
SetUint(x uint64)
SetPointer(x unsafe.Pointer)
SetString(x string)
Slice(i int, j int) Value
Slice3(i int, j int, k int) Value
String() string		//  Returns the value as a string type 
TryRecv() (x Value, ok bool)
TrySend(x Value) bool
Type() Type
Uint() uint64	//  It will be worth  uint  Type return , All unsigned integers can return 
UnsafeAddr() uintptr
assignTo(context string, dst *rtype, target unsafe.Pointer) Value
Convert(t Type) Value

1、 Get a specific type of value

// sclice
Index(i int) Value	//  Back to page i Elements , If i Crossing the border will collapse 

// struct
NumField() int		//  Returns the number of fields 
Field(i int) Value	//  Back to page i A field , If i Crossing the border will collapse 

// map
MapIndex(key Value) Value	//  return key Corresponding value 

// ptr
Elem() Value

// interface
//  Through the first  IsNil  Judge whether it is not empty ,  If it is not empty ,  Re pass  Elem()  Get dynamic values 

2、 Judge Value Whether the value in is valid :

// *int The null pointer of 
var a *int
fmt.Println("nil *int IsNil:", reflect.ValueOf(a).IsNil())
fmt.Println("nil *int IsValid:", reflect.ValueOf(nil).IsValid())

//  Instantiate a structure 
s := struct{
    }{
    }
//  Try to find a non-existent field from the structure 
fmt.Println(" There are no structural members  IsValid:", reflect.ValueOf(s).FieldByName("").IsValid())
//  Try to find a non-existent method from the structure 
fmt.Println(" There is no structural method  IsValid:", reflect.ValueOf(s).MethodByName("").IsValid())
//  Instantiate a map
m := map[int]int{
    }
//  Try from map Search for a key that doesn't exist 
fmt.Println(" Keys that don't exist  IsValid:", reflect.ValueOf(m).MapIndex(reflect.ValueOf(3)).IsValid())

Output :

nil *int IsNil: true
nil *int IsValid: false
 There are no structural members  IsValid: false
 There is no structural method  IsValid: false
 Keys that don't exist  IsValid: false

3、 Create instances with type information

var a int
myType := reflect.TypeOf(a)
myIns := reflect.New(myType)	// *int
myIns.Elem().SetInt(15)
fmt.Println(myIns.Type())
fmt.Println(myIns.Elem().Interface())

//  Output 
// *int
// 15

4、 Call functions through reflection

//  Ordinary function 
func add(a, b int) int {
    
    return a + b
}

func main() {
    
    //  Wrap the function as a reflection value object 
    funcValue := reflect.ValueOf(add)
    //  constructors parameters ,  Pass in two integer values 
    paramList := []reflect.Value{
    reflect.ValueOf(10), reflect.ValueOf(20)}
    //  Reflection calls function 
    retList := funcValue.Call(paramList)
    //  Get the first return value ,  Take the whole number 
    fmt.Println(retList[0].Int())
}

//  After the call is successful , adopt  retList[0]  Take the first parameter of the return value , Use  Int  Take the integer value of the return value .
// Call(in []Value) []Value



3、 ... and 、 Three laws of reflection

1、 Reflection can " Interface type variables " Convert to " Reflection type object ".

notes : Here, reflection type refers to reflect.Type and reflect.Value.

TypeOf And ValueOf The parameter of is an empty interface type , Any variable can be passed in .

func TypeOf(i interface{
    }) Type 
func ValueOf(i interface{
    }) Value 

2、 Reflection can " Reflection type object " Convert to " Interface type variables ".

reflect.Type It's an interface , So pass it on to fmt The function will print the underlying data .reflect.Value Yes interface() Method .

// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{
    }

var a int
fmt.Println(reflect.ValueOf(a).Interface())

3、 If you want to modify Reflection type object Its value must be Writable .

  Go The language is similar to xx.f[1] and *p Any form of expression can represent a variable , But others such as x + 1 and f(2) Is not a variable . A variable is an addressable memory space , It stores a value , And the stored value can be updated by memory address .

   writability The final decision is whether a reflective object stores the original value . For a without writability Of Value Type variable , call Set Method will report an error . We can go through CanSet() Method check a reflect.Value Type variable writability .CanAddr() Express Addressable , Addressable Value Not necessarily , You also need to determine whether it can be exported . such as , Private members in a structure are not exportable (unexported field).

Example :

var a int = 3
fmt.Printf("a CanAddr: %v\n", reflect.ValueOf(a).CanAddr())

v := reflect.ValueOf(&a).Elem()
fmt.Printf("v CanAddr: %v\n", v.CanAddr())
fmt.Printf("v CanSet: %v\n", v.CanSet())

v.SetInt(1)
fmt.Println(a)

Output :

a CanAddr: false
v CanAddr: true
v CanSet: true
1

版权声明
本文为[baboon_ chen]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204210546350991.html