Go part 7 反射

反射

反射是指在程序运行期间对程序自己进行访问和修改的能力,(程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时,程序没法获取自身的信息)编程

支持反射的语言能够在程序编译期间将变量的反射信息,如字段名称、类型等信息整合到可执行文件中,并给程序提供接口访问反射信息,这样就能够在程序运行期间获取类型的反射信息,而且有能力修改它们json

Go 程序在运行期间使用 reflect 包访问程序的反射信息数组

像 Python,JavaScript 类动态语言,因为自己的语法特性就可让代码运行期间访问程序自身的值和类型信息,所以不须要反射系统函数

 

反射类型对象(reflect.Type)

使用 reflect.TypeOf() 函数能够获取变量的反射类型对象(reflect.Type)ui

typeOfTest := reflect.TypeOf(test)

 

经过反射获取类型信息spa

经过反射类型对象能够获取自身的类型信息,使用 Name() 方法获取类型名称,使用 Kind() 方法获取类型归属的种类 指针

package main
import (
	"fmt"
	"reflect"
)

func GetReflectInfo(a interface{}) {
	// 获取变量 a 的类型对象,类型对象的类型是 reflect.Type
	var typeOfA reflect.Type = reflect.TypeOf(a)
	fmt.Printf("%T\n", typeOfA)
        // 打印类型名 和 种类
	fmt.Println(typeOfA.Name(), typeOfA.Kind())
}

func main() {
	GetReflectInfo("666")
}

运行结果:
*reflect.rtype
string string

发现类型和种类都是 string,很奇怪是否是,接着看下面的例子 ...对象

 

理解反射的类型(Type)和种类(Kind)blog

编程中,使用最多的是类型,但在反射中,当须要区分一个大品种的类型时,就会用到种类(Kind)索引

1)类型(Type)

类型指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字自定义的类型,自定义类型须要指定类型名称,例如,type A struct {},类型名就是 A

 

2)种类(Kind)

种类指的是对象归属的大品种,在 reflect 包中有以下定义:

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        // 底层指针
)

Map、Slice、Chan 属于引用类型,使用起来相似于指针,可是在种类常量定义中仍然属于独立的种类,不属于 Ptr

type A struct{} 结构体属于 Struct 种类,*A 属于 Ptr 种类

 

3)从自定义的类型对象中获取类型名称和类型种类(增强理解)

package main

import (
	"fmt"
	"reflect"
)

type Cat struct{}
type Enum int

func main(){
	var cat Cat = Cat{}
	var num Enum = 1

	//对 cat 变量使用反射
	var typeOfCat reflect.Type= reflect.TypeOf(cat)
	fmt.Println(typeOfCat.Name(), typeOfCat.Kind())

	//对 num 变量使用反射
	typeOfNum := reflect.TypeOf(num)
	fmt.Println(typeOfNum.Name(), typeOfNum.Kind())
}

运行结果:
Cat struct
Enum int

  

经过反射获取指针指向的元素类型

对指针类型的变量获取反射类型对象后,能够经过 reflect.Elem() 方法获取这个指针指向的元素类型,这个过程被称之为取元素,等效于对指针类型变量作了一个 * 操做

dmeo:cat 指针变量的反射类型对象的类型名称是 空字符串(为何不是 *cat),种类是 ptr,获取值以后的类型名是 cat,种类是 struct

package main
import (
	"fmt"
	"reflect"
)

type Cat struct {}

func GetPointerReflectInfo(a interface{}) {
	//获取指针类型的反射类型对象
	typeOfA := reflect.TypeOf(a)
	fmt.Printf("NameType:%T Name:%v Kind:%v\n", typeOfA.Name(), typeOfA.Name(), typeOfA.Kind())

	//取指针类型的元素
	typeOfA = typeOfA.Elem()
	fmt.Printf("Name:%v Kind:%v\n", typeOfA.Name(), typeOfA.Kind())
}

func main(){
	//建立 cat 类型的指针实例
	var cat *Cat = new(Cat)
	GetPointerReflectInfo(cat)
}

运行结果:
NameType:string Name: Kind:ptr
Name:Cat Kind:struct

  

经过反射获取结构体的字段信息

变量经过 reflect.TypeOf() 函数获取反射类型对象后,若是反射类型对象的种类是 struct,那么能够经过反射类型对象(reflect.Type)的 NumField() 和 Field() 等方法得到结构体字段的详细信息,以结构体字段类型(StructField)返回

种类是 struct 的反射类型对象(reflect.Type)能够经过调用下面的方法来获取:

结构体成员(字段)访问的方法列表
方法 说明
Field(i int) StructField 根据索引,返回索引对应的StructField。当值不是结构体或索引超界时发生宕机
NumField() int 返回结构体成员字段数量。当类型不是结构体或索引超界时发生宕机
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的StructField。没有找到时 bool 返回 false,当类型不是结构体或索引超界时发生宕机
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每一个结构体的字段索引,返回StructField。没有找到时返回零值。当类型不是结构体或索引超界时 发生宕机
FieldByNameFunc( match func(string) bool) (StructField,bool) 根据匹配函数匹配须要的字段。当值不是结构体或索引超界时发生宕机

  

结构体字段类型(StructField)也是一个结构体种类,里面包含的字段有 Name、PkgPath、Type 等,StructField 的结构以下:

type StructField struct {
    Name string          // 字段名
    PkgPath string       // 字段路径
    Type      Type       // 字段反射类型对象
    Tag       StructTag  // 字段的结构体标签
    Offset    uintptr    // 字段在结构体中的相对偏移
    Index     []int      // Type.FieldByIndex中的返回的索引值
    Anonymous bool       // 是否为匿名字段
}

 

demo:实例化一个结构体,并给字段赋值,而后获取该结构体变量的反射类型对象(reflect.Type),而后调用 FieldByName() 方法查找结构体中指定 Name 的字段,最后直接获取到了字段的结构体数据

package main
import (
	"fmt"
	"reflect"
)

type Cat struct {
	Name string
    Type int `json:"type" id:"100"`
}

func main(){
	//建立猫的结构体实例
	var cat Cat = Cat{"tom", 66}
	//获取反射类型对象
	typeOfCat := reflect.TypeOf(cat)

	//经过索引遍历结构体字段
	for i:=0; i<typeOfCat.NumField(); i++ {
		var catField reflect.StructField = typeOfCat.Field(i)
		fmt.Printf("%v, %v\n", catField.Name, catField.Tag)
	}

	//经过 Type 字段查找字段信息
	catTagField, ok := typeOfCat.FieldByName("Type")
	if ok {
		//从 Tag 字段中经过 Get() 方法获取到指定的 tag,没有取到默认为 空 string
		fmt.Printf("'%v', '%v', '%v'\n", catTagField.Tag.Get("json"), catTagField.Tag.Get("id"), catTagField.Tag.Get("nil"))
	}
}

运行结果:
Name, 
Type, json:"type" id:"100"
'type', '100', ''

  

反射值对象(reflect.Value)

反射不只能够获取变量的类型信息,还能够动态的获取 和 修改变量的值

使用 reflect.ValueOf() 函数获得变量的反射值对象(reflect.Value),进而获取 和 修改变量的值

valueOfTest := reflect.ValueOf(test)

  

从反射值对象获取值

相关文章
相关标签/搜索