欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

golang的reflection(转)

程序员文章站 2022-04-14 15:34:49
反射reflection 可以大大提高程序的灵活性,使得interface{}有更大的发挥余地 反射可以使用TypeOf和ValueOf函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字段的本质) 想要利用反射修改对象状态,前提是interface.data是settable,即po ......
作者:bgbiao
链接:https://www.jianshu.com/p/42c19f88df6c
來源:简书

反射reflection

  • 可以大大提高程序的灵活性,使得interface{}有更大的发挥余地
  • 反射可以使用typeof和valueof函数从接口中获取目标对象信息
  • 反射会将匿名字段作为独立字段(匿名字段的本质)
  • 想要利用反射修改对象状态,前提是interface.data是settable,即pointer-interface
  • 通过反射可以“动态”调用方法

常用的类型、函数和方法

//返回动态类型i的类型,如果i是一个空结构体类型,typeof将返回nil
func typeof(i interface{}) type

//type 接口类型
type type interface {
    align() int
    fieldalign() int
    //指定结构体中方法的下标,返回某个方法的对象,需要注意的是返回的method是一个独立的结构体
    method(int) method
    /*
    type method struct {
        name string
        pkgpath string
        type type
        func value
        index int
    }
    */


    methodbyname(string) (method, bool)

    //返回该结构体类型的方法下标
    nummethod() int
    //返回类型的名称,即动态类型i的名称
    name() string
    pkgpath() string
    size() uintptr
    string() string
    kind() kind
    implements(u type) bool
    assignableto(u type) bool
    convertibleto(u type) bool
    comparable() bool
    bits() int
    chandir() chandir
    isvariadic() bool
    elem() type
    //返回结构体类型第i个字段
    field(i int) structfield
    //structfield结构体
    //type structfield struct {
    // name string
    // pkgpath string
    // type type
    // tag  structtag
    // offset uintptr
    // index []int
    // anonymous bool

    //根据结构体字段索引获取嵌入字段的结构体信息
    fieldbyindex(index []int) structfield

    fieldbyname(name string) (structfield, bool)
    fieldbynamefunc(match func(string) bool) (structfield, bool)
    in(i int) type
    key() type
    len() int
    //返回动态类型i(结构体字段)的字段总数
    numfield() int
    numin() int
    numout() int
    out(i int) type
}

//返回接口i的一个初始化的新值.valueof(nil)返回一个零值
func valueof(i interface{}) value

// value结构体
type value struct {

}
// value结构体的一些方法
// 返回结构体v中的第i个字段。如果v的类型不是结构体或者i超出了结构体的范围,则会出现panic
func (v value) field(i int) value

//以接口类型返回v的当前值
func (v value) interface() (i interface{})
//等价于.
var i interface{} = (v's underlying value)


//通过反射方式修改结构体对象的一些方法

//返回接口v包含或者指针v包含的值
func (v value) elem() value
//判断该接口v是否可以被set修改
func (v value) canset() bool

//使用另外一个反射接口去修改反射值
func (v value) set(x value)
//其他不同类型的set
func (v value) setbool(x bool)
func (v value) setbytes(x []byte)
func (v value) setfloat(x float64)
func (v value) setint(x int64)
//设置结构体对象v的长度为n
func (v value) setlen(n int)
func (v value) setstring(x string)


//一些辅助方法
//返回反射结构体的value的类型.如果v为零值,isvalid将返回false
func (v value) kind() kind
//判断value是否为有效值,通常用在判断某个字段是否在反射体的value中
func (v value) isvalid() bool

//kind常量
type kind uint
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
)

 

反射的基本操作

通过反射来获取结构体字段的名称以及其他相关信息。

package main

import (
    "fmt"
    "reflect"
)

//定义结构体
type user struct {
    id   int
    name string
    age  int
}

//定义结构体方法
func (u user) hello() {
    fmt.println("hello xuxuebiao")
}

func main() {
    u := user{1, "bgops", 25}
    info(u)
    u.hello()
}

//定义一个反射函数,参数为任意类型
func info(o interface{}) {
    //使用反射类型获取o的type,一个包含多个方法的interface
    t := reflect.typeof(o)
    //打印类型o的名称
    fmt.println("type:", t.name())

    //使用反射类型获取o的value,一个空的结构体
    v := reflect.valueof(o)
    fmt.println("fields:")

    //t.numfield()打印结构体o的字段个数(id,name,age共三个)
    for i := 0; i < t.numfield(); i++ {
        //根据结构体的下标i来获取结构体某个字段,并返回一个新的结构体
        /**
        type structfield struct {
            name string
            pkgpath string
            type      type
            tag       structtag
            offset    uintptr
            index     []int
            anonymous bool
        }
        **/
        f := t.field(i)

        //使用结构体方法v.field(i)根据下标i获取字段value(id,name,age)
        //在根据value的interface()方法获取当前的value的值(interface类型)
        val := v.field(i).interface()
        fmt.printf("%6s:%v = %v\n", f.name, f.type, val)
    }

    //使用t.nummethod()获取所有结构体类型的方法个数(只有hello()一个方法)
    //接口type的方法nummethod() int
    for i := 0; i < t.nummethod(); i++ {
        //使用t.method(i)指定方法下标获取方法对象。返回一个method结构体
        //method(int) method
        m := t.method(i)
        //打印method结构体的相关属性
        /*
        type method struct {
              name    string
              pkgpath string
              type    type
              func    value
              index   int
        }
        */
        fmt.printf("%6s:%v\n", m.name, m.type)
    }
}

 

输出

type: user
fields:
    id:int = 1
  name:string = bgops
   age:int = 25
 hello:func(main.user)
hello xuxuebiao

 

注意:我们上面的示例是使用值类型进行进行反射构造的。如果是指针类型的话,我们需要使用reflect.struct字段进行判断接口类型的kind()方法

if k := t.kind();k != reflect.struct {
    fmt.println("非值类型的反射")
    return
}

 

匿名字段的反射以及嵌入字段

注意:反射会将匿名字段当做独立的字段去处理,需要通过类型索引方式使用fieldbyindex方法去逐个判断

//根据指定索引返回对应的嵌套字段
fieldbyindex(index []int) structfield

type structfield struct {
    name    string
    pkgpath string
    type    type
    tag     structtag
    offset  uintptr
    index   []int
    anonymous   bool //是否为匿名字段
}
package main

import (
    "fmt"
    "reflect"
)

type user struct {
    id   int
    name string
    age  int
}

type manager struct {
    user
    title string
}

func main() {
    //注意匿名字段的初始化操作
    m := manager{user: user{1, "biaoge", 24}, title: "hello biao"}
    t := reflect.typeof(m)

    fmt.printf("%#v\n", t.fieldbyindex([]int{0}))
    fmt.printf("%#v\n", t.fieldbyindex([]int{1}))
    fmt.printf("%#v\n", t.fieldbyindex([]int{0, 0}))
    fmt.printf("%#v\n", t.fieldbyindex([]int{0, 1}))

}

输出:

reflect.structfield{name:"user", pkgpath:"", type:(*reflect.rtype)(0x4ac660), tag:"", offset:0x0, index:[]int{0}, anonymous:true}
reflect.structfield{name:"title", pkgpath:"main", type:(*reflect.rtype)(0x49d820), tag:"", offset:0x20, index:[]int{1}, anonymous:false}
reflect.structfield{name:"id", pkgpath:"", type:(*reflect.rtype)(0x49d1a0), tag:"", offset:0x0, index:[]int{0}, anonymous:false}
reflect.structfield{name:"name", pkgpath:"", type:(*reflect.rtype)(0x49d820), tag:"", offset:0x8, index:[]int{1}, anonymous:false}

 

通过反射修改目标对象

通过反射的方式去修改对象的某个值。需要注意的亮点是,首先,需要找到对象相关的名称,其次需要找到合适的方法去修改相应的值。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := 123
    v := reflect.valueof(&x)
    v.elem().setint(5256)
    fmt.println(x)
}

 

输出:

5256
修改i的时候需要找到对象字段的名称;并且判断类型,使用相对正确的类型修改值
v.fieldbyname("name");f.kind() == reflect.string {
    f.setstring("test string")
}

判断是否找到正确的字段名称:

f := v.fieldbyname("name1")
//判断反射对象value中是否找到name1字段
if !f.isvalid() {
    fmt.println("bad field")
    return
}
package main

import (
    "fmt"
    "reflect"
)

type user struct {
    id   int
    name string
    age  int
}

//使用反射方式对结构体对象的修改有两个条件
//1.通过指针
//2.必须是可set的方法
func main() {
    num := 123
    numv := reflect.valueof(&num)
    //通过value的elem()和setx()方法可直接对相关的对象进行修改
    numv.elem().setint(666)
    fmt.println(num)

    u := user{1, "biao", 24}
    uu := reflect.valueof(&u)
    //set()后面的必须是值类型
    //func (v value) set(x value)
    test := user{2, "bgops", 2}
    testv := reflect.valueof(test)
    uu.elem().set(testv)
    fmt.println("change the test to u with set(x value)", uu)

    //此时的u已经被上面那个uu通过指针的方式修改了
    set(&u)
    fmt.println(u)
}

func set(o interface{}) {
    v := reflect.valueof(o)
    //判断反射体值v是否是ptr类型并且不能进行set操作
    if v.kind() == reflect.ptr && ! v.elem().canset() {
        fmt.println("xxx")
        return
        //初始化对象修改后的返回值(可接受v或v的指针)
    } else {
        v = v.elem()
    }
    //按照结构体对象的名称进行查找filed,并判断类型是否为string,然后进行set
    if f := v.fieldbyname("name"); f.kind() == reflect.string {
        f.setstring("byby")
    }
}

输出:

666
change the test to u with set(x value) &{2 bgops 2}
{2 byby 2}

通过反射进行动态方法的调用

使用反射的相关知识进行方法的动态调用

package main

import (
    "fmt"
    "reflect"
)

type user struct {
    id   int
    name string
    age  int
}

func (u user) hello(name string, id int) {
    fmt.printf("hello %s,my name is %s and my id is %d\n", name, u.name, id)
}

func main() {
    u := user{1, "biaoge", 24}
    fmt.println("方法调用:")
    u.hello("xuxuebiao", 121)

    //获取结构体类型u的value
    v := reflect.valueof(u)
    //根据方法名称获取value中的方法对象
    mv := v.methodbyname("hello")

    //构造一个[]value类型的变量,使用value的call(in []value)方法进行动态调用method
    //这里其实相当于有一个value类型的slice,仅一个字段
    args := []reflect.value{reflect.valueof("xuxuebiao"), reflect.valueof(5256)}
    fmt.println("通过反射动态调用方法:")
    //使用value的call(in []value)方法进行方法的动态调用
    //func (v value) call(in []value) []value
    //需要注意的是当v的类型不是func的化,将会panic;同时每个输入的参数args都必须对应到hello()方法中的每一个形参上
    mv.call(args)

}
方法调用:
hello xuxuebiao,my name is biaoge and my id is 121
通过反射动态调用方法:
hello xuxuebiao,my name is biaoge and my id is 5256