反射可以在运行时获取对象的类型信息和内存结构。反射操作所需的全部信息来自于接口变量,接口变量除存储自身类型外,还会保存实际对象的类型信息。
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
类型
Name & Kind
Name
获取静态类型,Kind
获取的是底层类型;在自定义类型的变量中获取到的值是不一样的。
type NewInt int
var x NewInt = 100
t := reflect.TypeOf(x)
fmt.Println(t, t.Name(), t.Kind()) // main.NewInt NewInt int
Kind
支持的类型如下所示:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
指针类型
传入对象分区为基类型和指针类型,他们并不属于同一类型。方法Elem
返回指针、数组、切片、通道、字典(值)的基类型。
type NewInt int
var x NewInt = 100
tx, tp := reflect.TypeOf(x), reflect.TypeOf(&x)
fmt.Println(tx.Kind(), tp.Kind(), tx == tp, tx == tp.Elem()) // int ptr false true
不同基类型的指针也是不等的
var x rune = 100
var y uint32 = 100
tx, ty := reflect.TypeOf(&x), reflect.TypeOf(&y)
fmt.Println(tx.Kind(), ty.Kind(), tx == ty, tx.Elem() == ty.Elem()) // ptr ptr false false
结构体类型
只有在获取结构体指针的基类型后才能对其遍历
type user struct {
name string
age int
}
type manager struct {
user
title string
}
func main() {
m := &manager{user{"Jack", 18}, "boss"}
t := reflect.TypeOf(m)
if t.Kind() == reflect.Ptr { // 获取指针的基类型
t = t.Elem()
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Println(f.Type)
if f.Anonymous { // 输出匿名字段信息(user)
for j := 0; j < f.Type.NumField(); j++ {
subf := f.Type.Field(j)
fmt.Println(subf.Type)
}
}
}
}
/*
main.user
string
int
string
*/
Field
和NumField
可以访问当前包或者外包的非导出结构成员。
var s http.Server
t := reflect.TypeOf(s)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
// Addr,Handler,TLSConfig,ReadTimeout,ReadHeaderTimeout,WriteTimeout,IdleTimeout,MaxHeaderBytes,TLSNextProto,ConnState,ErrorLog,BaseContext,ConnContext,disableKeepAlives,inShutdown,nextProtoOnce,nextProtoErr,mu,listeners,activeConn,doneChan,onShutdown,
对于匿名字段,也可以通过FieldByName
和FieldByIndex
直接访问
type user struct {
name string
age int
}
type manager struct {
user
title string
}
func main() {
m := manager{user{"Jack", 18}, "boss"}
t := reflect.TypeOf(m)
name, exists := t.FieldByName("name") // 多级访问时无法访问嵌套的同名字段
if exists {
fmt.Println(name.Type) // string
}
sex, exists := t.FieldByName("sex")
if exists {
fmt.Println(sex.Type)
}
age := t.FieldByIndex([]int{0, 1}) // 支持多级访问
fmt.Println(age.Type)
}
输出方法集(FIX)
struct tag
利用反射提取struct tag
,常用于ORM映射或者数据格式验证。
type user struct {
name string `field:"name" type:"varchar(50)"`
age int `field:"age" type:"int"`
}
func main() {
var u user
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
attr := t.Field(i)
fmt.Println(attr.Tag.Get("field"), attr.Tag.Get("type"))
}
}
// name varchar(50)
// age int