Go语言的反射
在G语言程序中可以通过reflect包的TypeOf()函数获取任意值的类型对象,程序可以通过类型对象访问任意值的类型信息。
package main
import (
"fmt"
"reflect"
)
func main() {
var a int
typeOfA := reflect.TypeOf(a)
fmt.Printf("%v, %v",typeOfA.Name(),typeOfA.Kind())
}
这里定义一个int型变量
然后获取它的反射类型得到typeOfA对象
通过typeOfA的成员函数Name(),和Kind()获取变量的类型和种类
反射的类型和种类
在编程中使用最多的就是类型了,但是在反射中为了区分一个大的品种的类型时会使用到种类。例如需要统一判断类型中的指针时,使用种类(kind)信息可以方便一些。
种类指的是对象归属的品种
在reflect中有如下种类
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属于引用类型,使用起来类似于指针,但是在种类定义中仍然属于独立的种类,不属于指针。
从类型对象中获取类型名称和种类示例
package main
import (
"fmt"
"reflect"
)
//定义一个Enum类型
type Enum int
const (
Zero = 0
)
func main(){
//声明一个空结构体
type cat struct {
}
//获取结构体反射类型对象
TypeOfCat := reflect.TypeOf(cat)
//显示反射类型对象的类型和种类
fmt.Printf("name: %v, kind: %v",TypeOfCat.Name(), TypeOfCat.Kind())
//获取Zero常量的反射类型对象
TypeOfA := reflect.TypeOf(Zero)
//打印输出Zero反射类型的类型和种类
fmt.Printf("Zero.name: %v, Zero.kind: %v",TypeOfA.Name(),TypeOfA.Kind())
}
以上代码实现对常量和结构体进行反射类型获取
声明了空结构体cat并对其实例化
使用reflect.TypeOf()获取实例化后cat的反射类型对象
然后输出cat的类型和种类
对常量Zero做同样操作
指针与指针指向的元素
Go程序对指针获取对象时,可以通过reflect.Elem()方法获取这个指针指向的元素类型,这个获取过程叫做取元素。等效于对指针做一个“*”操作。
取元素代码示例
package main
import (
"fmt"
"reflect"
)
func main() {
type cat struct{
}
//实例化结构体
ins := new(cat)
//获取反射对象
TypeOfCat := reflect.TypeOf(ins)
//打印反射对象类型和种类
fmt.Printf("name: '%v', kind: '%v'\n",TypeOfCat.Name(),TypeOfCat.Kind())
//获取元素
TypeOfCat := TypeOfCat.Elem()
//显示反射类型对象的名称和种类
fmt.Printf("name:'%v', kind: '%v'",TypeOfCat.Name(), TypeOfCat.Kind())
}
以上代码中首先创建了一个cat的空结构体,这里ins是一个*cat类型的指针变量。获取反射对象后先输出一下对象的类型和种类,这里的输出结果为name: ’ ', kind: ‘ptr’
从输出结果可以看出来指针变量的类型名为空,种类为Ptr,Go程序中反射中对指针变量的所有种类都是Ptr
然后通过反射对象的内部函数Elem()获取元素
再进行打印指针变量指向元素的类型和种类
这里输出结果为name: ‘cat’, kind: ‘struct’
这里就能看到类型名为cat,种类为struct
使用反射获取结构体成员类型
任意值可以通过reflect.TypeOf()方法获取反射对象信息,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()和Field()方法获取结构体成员的详细信息。
| 方法 |说明 |
|Field(i int) StructField|根据索引,返回索引对应的结构体字段信息。当值不是结构体或索引超界时发生宕机|
| NumField() int|返回结构体成员字段数量。当类型不是结构体或者索引超边界时引发宕机 |
|FieldByName(name string) (StructField,bool)| 根据给定字符串对应的结构体字段信息。没有找到时bool返回false,当类型不是结构体或者索引超界时发生宕机 |
|FieldByIndex(index []int) StructField|多层成员访问时,根据[]int提供的每个结构体的字段索引,返回字段信息,没有找到时返回零值。当类型不是结构体2或者索引超界时发生宕机|
|FieldByNameFunc(match func(string) bool) (StructField,bool)|根据匹配函数匹配需要的字段。当值不是结构体或索引超届时发生宕机 |
结构体字段类型
reflect.Type的Field()方法返回StructField结构,这个结构描述结构体的成员信息,通过这个信息可以获取成员与结构体的关系,如偏移量、索引、是否为匿名字段,结构体标签(Struct Tag)等,而且还可以通过StructField的Type字段进一步获取结构体成员的类型信息。
StructField的结构为
type StructField struct {
Name string //字段名
PkgPath string //字段路径
Tag StructTag //字段的结构体标签
Type Type //字段反射类型对象
Offset uintptr //字段在结构体中相对偏移
Index []int //Type.FieldByIndex中的返回的索引值
Anonymous bool //是否为匿名字段
}
获取成员反射信息代码示例
package main
import (
"fmt"
"reflect"
)
func main() {
//声明一个结构体
type cat struct {
Name string
//带有结构体标签的字段
Type int 'json:"type" id:"100"'
}
//创建cat实例
ins := cat{Name:"hzc",Type:1}
//获取结构体实例的反射类型
TypeOfCat := reflect.TypeOf(ins)
//遍历结构体所有成员
for i := 0; i < TypeOfCat.Numfield(); i++ {
//获取每个成员的结构体字段类型
typeOf := TypeOfCat.Field(i)
//输出成员名和tag
fmt.Printf("name: '%v', tag: '%v'\n",typeOf.Name,typeOf.Tag)
//通过字段名找到字段类型信息
if catType, ok := TypeOfCat.FieldByName("Type"); ok {
//从tag中取出需要的tag
fmt.Println(catType.Tag.Get("json"),catType.Tag.Get("id"))
}
}
}
以上代码实例化一个结构体并遍历其成员,再通过 reflect.Type的FieldBtName()方法查找结构体中指定名称的字段,直接获取信息。
结构体标签(StructTag)–对结构体字段的额外信息标签
通过reflect.Type获取结构体成员信息reflect.StructField结构中的Tag被称为结构体标签(Struct Tag)
结构体标签的格式
key1:“value1” key2:“value2”
结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔。
从结构体标签中获取值
func (tag StructTag)Get(key string) string{}
根据tag中的键获取对应的值,例如’key1:“value1” key2:“value2”‘的Tag中,可以传入"key1"获得"value1"
func (tag StructTag) LookUp(key string) (value string, ok bool){}
根据键查询值是否存在,存在则返回值ok返回true,不存在则值返回空ok返回false。