反射

反射可以在运行时获取对象的类型信息和内存结构。反射操作所需的全部信息来自于接口变量,接口变量除存储自身类型外,还会保存实际对象的类型信息。

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
*/

FieldNumField可以访问当前包或者外包的非导出结构成员。

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,

对于匿名字段,也可以通过FieldByNameFieldByIndex直接访问

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

你可能感兴趣的:(反射)