Go语言学习笔记之反射用法详解

2020-01-28 12:41:26王旭

    }
}

输出:


*main.B :
  {ap main func(*main.B) <func(*main.B) Value> 0}
  {av main func(*main.B) <func(*main.B) Value> 1}
  {bp main func(*main.B) <func(*main.B) Value> 2}
  {bv main func(*main.B) <func(*main.B) Value> 3}   
main.B :
  {av main func(*main.B) <func(*main.B) Value> 0} 
  {bv main func(*main.B) <func(*main.B) Value> 1}

有一点和想象的不同,反射能探知当前包或外包的非导出结构成员。
import (
    "net/http"
    "reflect"
    "fmt"
)
func main()  {
    var s http.Server
    t := reflect.TypeOf(s)
    for i := 0; i < t.NumField(); i++ {
        fmt.Println(t.Field(i).Name)
    }
}

输出:


Addr
Handler
ReadTimeout
WriteTimeout
TLSConfig
MaxHeaderBytes
TLSNextProto
ConnState
ErrorLog
disableKeepAlives
nextProtoOnce
nextProtoErr

相对 reflect 而言,当前包 和 外包 都是“外包”。
可用反射提取 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++ {
        f := t.Field(i)
        fmt.Printf("%s: %s %sn", f.Name, f.Tag.Get("field"), f.Tag.Get("type"))
    }
}
输出:


name: name varchar(50)
age: age int

辅助判断方法 Implements()、ConvertibleTo、AssignableTo() 都是运行期进行 动态调用 和 赋值 所必需的。
type X int
func (X) String() string {
    return ""
}
func main()  {
    var a X
    t := reflect.TypeOf(a)
    // Implements 不能直接使用类型作为参数,导致这种用法非常别扭
    st := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
    fmt.Println(t.Implements(st))
    it := reflect.TypeOf(0)
    fmt.Println(t.ConvertibleTo(it))
    fmt.Println(t.AssignableTo(st), t.AssignableTo(it))
}
输出:


true
true
true false

二、值(Value)

和 Type 获取类型信息不同,Value 专注于对象实例数据读写。
在前面章节曾提到过,接口变量会复制对象,且是 unaddressable 的,所以要想修改目标对象,就必须使用指针。