Go编程之反射reflect

Admin 2022-05-23 17:45:16 GoLang

反射是程序执行时检查其所拥有的结构,尤其是类型的一种能力,这是元编程的一种形式,同时也是造成混淆的来源。

每一种语言的反射模型都不同(很多语言根本不支持反射,Python通过hasattr方法实现)Go语言中的反射通过reflect包实现,实现了运行时反射,允许程序操作任意类型的对象。

reflect包中的两个数据类Type和Value:

Type:表示一个Go类型(不是所有Go类型的Type值都能使用所有方法,可参见每个方法的文档获取使用限制,在调用有分类限定的方法时,应先使用Kind方法获知类型的分类,调用该分类不支持的方法会导致运行时的panic)。

package main

import (
    "reflect"
    "fmt"
)

type S interface {
    Get()
    Post()
}

type A struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

func (a *A) Get() {
    fmt.Println("Get is Name: ", a.Name)
}

func (a *A) Post()  {
    fmt.Println("Post is Name: ", a.Name)
}

func main() {
    p := A{Name: "ywfuns.com", Value: "www"}
    n := new(S)

    k := reflect.TypeOf(p)
    fmt.Println(k.Name())   // 对象名称,不含包名;即A
    fmt.Println(k.String()) // 对象名称,含包名;即main.A
    fmt.Println(k.PkgPath()) // 包名
    fmt.Println(k.Kind()) // 对象类型,即struct
    fmt.Println(k.Size()) // 存储的字节大小
    fmt.Println(k.NumField()) // 获取字段数量
    fmt.Println(k.Field(0).Tag) // 根据索获取字段Tag
    fmt.Println(k.FieldByName("Name")) // 根据名称获取字段
    fmt.Println(k.FieldByIndex([]int{0}))  // 根据索引链获取嵌套字段

    // 引用类型需要用Elem()获取指针所指的对象类型
    h := reflect.TypeOf(n).Elem()
    fmt.Println(h.NumMethod()) // 可访问的方法数(仅对接口类型统计)
    fmt.Println(h.Method(0), h.Method(0).Name)
    fmt.Println(h.MethodByName("A"))
}

Value: 表示类型的值(不是所有Go类型的Value值都能使用所有方法,可参见每个方法的文档获取使用限制,在调用有分类限定的方法时,应先使用Kind方法获知类型的分类,调用该分类不支持的方法会导致运行时的panic)。

案例1:

package main

import (
    "reflect"
    "fmt"
)

func main()  {
    s := "www"
    val := reflect.ValueOf(s)

    fmt.Println(val.String()) // 返回值
    fmt.Println(val.Kind().String()) // 返回类型

    s1 := reflect.ValueOf(&s)        // 获取Value类型
    s1.Elem().SetString("blog")     // 设置值

    fmt.Println(s, s1.Elem().String())
}

案例2:

package main

import (
    "reflect"
    "fmt"
)

type B struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

func main()  {
    s := B{Name: "www", Value: "ywfuns.com"}
    val := reflect.ValueOf(s)

    fmt.Println(val.NumField())
    fmt.Println(val.Field(0))
    fmt.Println(val.FieldByName("Name"))

    m := reflect.ValueOf(&s).Elem()
    m.FieldByName("Name").SetString("blog")

    fmt.Println(s.Name, m.Field(0))
}

案例3:

通过反射调用结构体中的方法,通过reflect.Value.Method(i int).Call()或者reflect.Value.MethodByName(name string).Call()实现

package main

import (
    "fmt"
    "reflect"
)

type C struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

func (c *C) Get()  {
    fmt.Println("Get is", c.Name)
}

func (c *C) Set(s string)  {
    c.Name = s
    fmt.Println("Set is ", c.Name)
}

func main()  {
    s := &C{Name: "www", Value: "ywfuns"}
    val := reflect.ValueOf(s) // 也可以使用reflect.ValueOf(&s).Elem()
    val.MethodByName("Get").Call(nil)

    p := make([]reflect.Value, 1)
    p[0] = reflect.ValueOf("blog")
    val.MethodByName("Set").Call(p)  // 通过名称调用

    p[0] = reflect.ValueOf("Test")
    val.Method(1).Call(p) // 通过索引调用

    fmt.Println(s.Name, s.Value)
}
相关文章
最新推荐