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.Type
and the reflect.Value
two parts, and reflect package provided reflect.TypeOf
and reflect.ValueOf
two 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 reflect
Kind 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.Value
the type, which contains the value information of the original value. reflect.Value
Between the original value and can be interchangeable.
reflect.Value
Obtain 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.Type
In 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
StructField
It is used to describe a type of information in the field structure.
StructField
It 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.
- 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.
- Extensive use of reflected code is usually difficult to understand.
- Poor reflection properties, typically one to two orders of magnitude slower reflected codes based implementation code runs than the normal speed.
Exercises
- Write code using reflection to realize an ini file parser program.