The basis of language reflecting Go

This article describes the basic meaning and use the Go language reflected.

The internal mechanism of variables

Go language variable is divided into two parts:

  • Type of information: predefined meta information.
  • Value: The process can be run dynamically changing.

Reflection introduction

Reflection is the ability to access and modify the program itself in the program run. Program at compile time, the variable is converted to a memory address, variable names are not written to the compiler executable section. When you run the program, the program can not obtain their own information.

Language support reflected in the program can compile the information reflected variables, such as the field name, type information, structure information integrated into the executable file, and the program provides an interface to access information reflected, so that you can get in a program run type reflection information, and the ability to modify them.

Go program uses information reflect reflection packet access program at runtime.

In a previous blog we introduced the empty interface. Empty interface can store any type of variable, then how do we know what the air interface to the saved data is it? Reflection is acquired dynamically at runtime type of a variable information and value information.

reflect package

Go reflection in language, any interface value is by 一个具体类型and 具体类型的值composed of two parts (the concepts we have introduced an interface on a blog). Go reflected related functions provided by a language built reflect package, an arbitrary value in the interface can be understood as a reflection reflect.Typeand the reflect.Valuetwo parts, and reflect package provided reflect.TypeOfand reflect.ValueOftwo arbitrary object to obtain the functions and Value Type.

TypeOf

, In Go, a reflect.TypeOf()function type object can be obtained (reflect.Type) an arbitrary value, the program can access the type of information to any value by the type of the object.

package main

import (
    "fmt"
    "reflect"
)

func reflectType(x interface{}) {
    v := reflect.TypeOf(x)
    fmt.Printf("type:%v\n", v)
}
func main() {
    var a float32 = 3.14
    reflectType(a) // type:float32
    var b int64 = 100
    reflectType(b) // type:int64
}

type name和type kind

In reflection on further divided into two types: 类型(Type)and 种类(Kind). Go language because we can use a lot of custom configuration type keyword type, and 种类(Kind)refers to the type of bottom, but in reflection, when it is necessary to distinguish between a large variety of types of pointers, structures, etc., will be used 种类(Kind). For example, we define two types of pointers and two types of structures, their types and kinds of view by reflection.

package main

import (
    "fmt"
    "reflect"
)

type myInt int64

func reflectType(x interface{}) {
    t := reflect.TypeOf(x)
    fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind())
}

func main() {
    var a *float32 // 指针
    var b myInt    // 自定义类型
    var c rune     // 类型别名
    reflectType(a) // type: kind:ptr
    reflectType(b) // type:myInt kind:int64
    reflectType(c) // type:int32 kind:int32

    type person struct {
        name string
        age  int
    }
    type book struct{ title string }
    var d = person{
        name: "沙河小王子",
        age:  18,
    }
    var e = book{title: "《跟小王子学Go语言》"}
    reflectType(d) // type:person kind:struct
    reflectType(e) // type:book kind:struct
}

The image reflected in Go arrays, slices, the Map, a variable of type pointer or the like, they .Name()are returned .

In reflectKind package type definition is as follows:

type Kind uint
const (
    Invalid Kind = iota  // 非法类型
    Bool                 // 布尔型
    Int                  // 有符号整型
    Int8                 // 有符号8位整型
    Int16                // 有符号16位整型
    Int32                // 有符号32位整型
    Int64                // 有符号64位整型
    Uint                 // 无符号整型
    Uint8                // 无符号8位整型
    Uint16               // 无符号16位整型
    Uint32               // 无符号32位整型
    Uint64               // 无符号64位整型
    Uintptr              // 指针
    Float32              // 单精度浮点数
    Float64              // 双精度浮点数
    Complex64            // 64位复数类型
    Complex128           // 128位复数类型
    Array                // 数组
    Chan                 // 通道
    Func                 // 函数
    Interface            // 接口
    Map                  // 映射
    Ptr                  // 指针
    Slice                // 切片
    String               // 字符串
    Struct               // 结构体
    UnsafePointer        // 底层指针
)

ValueOf

reflect.ValueOf()It returns reflect.Valuethe type, which contains the value information of the original value. reflect.ValueBetween the original value and can be interchangeable.

reflect.ValueObtain the original value of the type of method are as follows:

method Explanation
Interface() interface {} The values ​​interface {} return type, the type can be converted to the specified type assertion
Int() int64 The return value is an int, all signed integer can return this way
Uint() uint64 The uint type value to return, all unsigned integer can return this way
Float() float64 The value of the double precision (float64) return type, all floating point numbers (float32, float64) can be returned in this manner
Bool() bool The value is returned as type bool
Bytes() []bytes The value of the byte array [] bytes type Returns
String() string The return value is a string type

Obtaining values ​​reflecting

func reflectValue(x interface{}) {
    v := reflect.ValueOf(x)
    k := v.Kind()
    switch k {
    case reflect.Int64:
        // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
        fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
    case reflect.Float32:
        // v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
        fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
    case reflect.Float64:
        // v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
        fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
    }
}
func main() {
    var a float32 = 3.14
    var b int64 = 100
    reflectValue(a) // type is float32, value is 3.140000
    reflectValue(b) // type is int64, value is 100
    // 将int类型的原始值转换为reflect.Value类型
    c := reflect.ValueOf(10)
    fmt.Printf("type c :%T\n", c) // type c :reflect.Value
}

By setting the value of variable reflection

By modifying the value of desired variable reflection in a function, it is noted that the value of the function parameter passing copy variable address must be passed in order to modify variable values. It reflected using a proprietary Elem()method to get a pointer to the corresponding value.

package main

import (
    "fmt"
    "reflect"
)

func reflectSetValue1(x interface{}) {
    v := reflect.ValueOf(x)
    if v.Kind() == reflect.Int64 {
        v.SetInt(200) //修改的是副本,reflect包会引发panic
    }
}
func reflectSetValue2(x interface{}) {
    v := reflect.ValueOf(x)
    // 反射中使用 Elem()方法获取指针对应的值
    if v.Elem().Kind() == reflect.Int64 {
        v.Elem().SetInt(200)
    }
}
func main() {
    var a int64 = 100
    // reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value
    reflectSetValue2(&a)
    fmt.Println(a)
}

isNil()和isValid()

isNil ()

func (v Value) IsNil() bool

IsNil()The reported values ​​v holds whether nil. Classification value of v must be held by channel, function, interface, maps, pointers, one slice; otherwise IsNil function causes panic.

isValid()

func (v Value) IsValid() bool

IsValid()V whether to hold a return value. If v is a value of zero Value returns false, then v except IsValid, String, Kind of methods will lead to panic.

for example

IsNil()Often used for determining whether the pointer is null; IsValid()is often used to determine whether a valid return value.

func main() {
    // *int类型空指针
    var a *int
    fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
    // nil值
    fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
    // 实例化一个匿名结构体
    b := struct{}{}
    // 尝试从结构体中查找"abc"字段
    fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
    // 尝试从结构体中查找"abc"方法
    fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
    // map
    c := map[string]int{}
    // 尝试从map中查找一个不存在的键
    fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
}

Reflector structure

Structure associated with the method

By arbitrary value reflect.TypeOf()obtained after reflection object information, if it is the type of structure, the value of an object by reflection ( reflect.Type) of NumField()and Field()methods for details of the structure members.

reflect.TypeIn the related method of obtaining a structure member shown in the following table.

method Explanation
Field(i int) StructField The index structure to return information corresponding to the index field.
NumField() int Returns the number of fields structure members.
FieldByName(name string) (StructField, bool) The return information structure corresponding to the character fields given string.
FieldByIndex(index []int) StructField Multilayer member access, in accordance with [] of each field index structure provides int returns information field.
FieldByNameFunc(match func(string) bool) (StructField,bool) According to incoming matching function to match the required fields.
NumMethod() int Returns the number of methods centralized approach of this type
Method(int) Method The method returns the type of the i-th method set
MethodByName(string)(Method, bool) Method Name Return type method the concentration according to the method

StructField type

StructFieldIt is used to describe a type of information in the field structure.

StructFieldIt is defined as follows:

type StructField struct {
    // Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
    // 参见http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string
    Type      Type      // 字段的类型
    Tag       StructTag // 字段的标签
    Offset    uintptr   // 字段在结构体中的字节偏移量
    Index     []int     // 用于Type.FieldByIndex时的索引切片
    Anonymous bool      // 是否匿名字段
}

Example reflector structure

When we get a structure to use reflection data field information which can be acquired by sequentially indexing, may be designated to acquire the information field name field.

type student struct {
    Name  string `json:"name"`
    Score int    `json:"score"`
}

func main() {
    stu1 := student{
        Name:  "小王子",
        Score: 90,
    }

    t := reflect.TypeOf(stu1)
    fmt.Println(t.Name(), t.Kind()) // student struct
    // 通过for循环遍历结构体的所有字段信息
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
    }

    // 通过字段名获取指定结构体字段信息
    if scoreField, ok := t.FieldByName("Score"); ok {
        fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
    }
}

Next, a write function printMethod(s interface{})method comprising s to traverse the print.

// 给student添加两个方法 Study和Sleep(注意首字母大写)
func (s student) Study() string {
    msg := "好好学习,天天向上。"
    fmt.Println(msg)
    return msg
}

func (s student) Sleep() string {
    msg := "好好睡觉,快快长大。"
    fmt.Println(msg)
    return msg
}

func printMethod(x interface{}) {
    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    fmt.Println(t.NumMethod())
    for i := 0; i < v.NumMethod(); i++ {
        methodType := v.Method(i).Type()
        fmt.Printf("method name:%s\n", t.Method(i).Name)
        fmt.Printf("method:%s\n", methodType)
        // 通过反射调用方法传递的参数必须是 []reflect.Value 类型
        var args = []reflect.Value{}
        v.Method(i).Call(args)
    }
}

Reflection is double-edged sword

Reflection is a powerful and expressive tool that allows us to write more flexible code. But reflection should not be abused, there are three reasons.

  1. Reflection-based code that is extremely fragile, reflected in the types of errors that pose a real panic when run, it is likely to be a long time after the finish of the code.
  2. Extensive use of reflected code is usually difficult to understand.
  3. Poor reflection properties, typically one to two orders of magnitude slower reflected codes based implementation code runs than the normal speed.

Exercises

  1. Write code using reflection to realize an ini file parser program.

Guess you like

Origin www.cnblogs.com/nickchen121/p/11517446.html