当前位置: 移动技术网 > IT编程>脚本编程>Go语言 > golang的reflection(转)

golang的reflection(转)

2018年11月15日  | 移动技术网IT编程  | 我要评论

惠灵顿天气,李姝妍,石家庄铁道大学四方学院怎么样

作者: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

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网