reflect使用

reflect概述

反射是一种检查存储在 接口变量中的<值,类型>对 的机制,借助go反射包提供的reflect.TypeOfreflect.ValueOf可以方便的访问到一个接口值的reflect.Typereflect.Value部分,从而可进一步得到 这个接口的结构类型和对其进行值的修改操作

反射的使用

  1. 获取接口对象的字段,类型和方法信息
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age  int
}

func (p Person)SetName(name string){
}
func (p Person)SetAge(age int){
    p.Age = age
}

func Info(o interface{}) {
    t := reflect.TypeOf(o)         //获取接口的类型
    fmt.Println("Type:", t.Name()) //t.Name() 获取接口的名称

    if t.Kind() != reflect.Struct { //通过Kind()来判断反射出的类型是否为需要的类型
        fmt.Println("err: type invalid!")
        return
    }

    v := reflect.ValueOf(o) //获取接口的值类型
    fmt.Println("Fields:")

     // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
    // 2. 再通过reflect.Type的Field获取其Field
    // 3. 最后通过Field的Interface()得到对应的value

    for i := 0; i < t.NumField(); i++ { //NumField取出这个接口所有的字段数量
        f := t.Field(i)                                   //取得结构体的第i个字段
        val := v.Field(i).Interface()                     //取得字段的值
        fmt.Printf("%6s: %v = %v\n", f.Name, f.Type, val) //第i个字段的名称,类型,值
    }

    for i := 0; i < t.NumMethod(); i++ {
        m := t.Method(i)
        fmt.Printf("%6s: %v\n", m.Name, m.Type) //获取方法的名称和类型
    }
}

func main() {
    inputs := Person{"sbd", 12}
    inputs.SetAge(11)
    Info(inputs)
}

result:

Type: Person
Fields:
  Name: string = sbd
   Age: int = 12
SetAge: func(main.Person, int)
SetName: func(main.Person, string)

interfacereflect.TypeUser,不是*User, 因此

func (p *Person)SetName(name string){
}

不会被反射出来

  1. 获取接口对象的类型名称,通过refelct.TypeOf()获取接口对象的类型,并通过Name()方法获取接口的名称。
  2. 获取对象中所有字段的名称,类型和值,通过reflect.ValueOf()获取接口对象的值类型取得字段的名称和类型,然后通过v.Field(i).Interface()取得第i个字段的值。
  3. 还可以通过NumMethod()循环获取接口对象所有方法的名称和类型。
  1. 反射接口对象中的匿名或嵌入字段信息
    先再添加一个Manager结构,User作为它的匿名字段
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age  int
}

type Manage struct{
    Person
    Title string
}


func Info(m interface{}) {
    t := reflect.TypeOf(m)
    fmt.Println("Type:", t.Name())

    //t := refelct.TypeOf(m)将Manager的字段类型取出来,在反射中对象字段是通过索引取到的,所以可通过t.Field(0)
    fmt.Printf("%#v\n", t.Field(0))

    fmt.Printf("%+v\n", t.FieldByIndex([]int{0,1}))
}

func main() {
    inputs := Manage{Person{"xudong", 18}, "yuwen"}
    Info(inputs)

}

1.给FieldByIndex()传入一个int型的slice索引,如FieldByIndex([]int{0,0})即取得User结构体中的Id字段。
2.通过FieldByName("Id")也可以取得User结构体中Id字段。

  1. 通过反射修改对象
    上面通过reflect.TypeOfreflect.ValueOf已经可以得到接口对象的类型,字段和方法等属性了,怎么通过反射来修改对象的字段值?
func main() {
        x := 100
    v := reflect.ValueOf(&x)
    v.Elem().SetInt(200)
    fmt.Println(x)
}
200

要修改变量x的值,首先就要通过reflect.ValueOf来获取x的值类型,refelct.ValueOf返回的值类型是变量x一份值拷贝,要修改变量x就要传递x的地址,从而返回x的地址对象,才可以进行对x变量值对修改操作。在得到x变量的地址值类型之后先调用Elem()返回地址指针指向的值的Value封装。然后通过Set方法进行修改赋值。

通过反射可以很容易的修改变量的值,怎么来修改结构体中的字段值?

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age  int
}

func SetInfo(o interface{}) {
    v := reflect.ValueOf(o)

    if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { //判断是否为指针类型 元素是否可以修改
        fmt.Println("cannot set")
        return
    } else {
        v = v.Elem() //实际取得的对象
    }

    //判断字段是否存在
    f := v.FieldByName("Name")
    if !f.IsValid() {
        fmt.Println("wuxiao")
        return
    }

    //设置字段
    if f := v.FieldByName("Name"); f.Kind() == reflect.String {
        f.SetString("BY")
    }
}

func main() {
    inputs := Person{"sbd", 12}
    SetInfo(&inputs)

    fmt.Printf("%+v", inputs)
}

要成功修改结构体中的某个字段,主要进行以下操作:

  1. 首先要反射出这个字段的地址值类型;
  2. 判断反射返回类型是否为reflect.Ptr指针类型(通过指针才能操作对象地址中的值)同时还要判断这个元素是否可以修改;
  3. 通过FieldByName的返回值判断字段是否存在
  4. 通过Kind()和Set来修改字段的值
  1. 通过反射“动态”调用方法
    现在已经可以通过反射获取并修改接口对象的字段,类型等信息了,那怎么通过反射“动态”调用接口对象等方法?
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age  int
}

func (p Person) GetInfo(v Person) (name string, age int) {
    return p.Name, p.Age
}

func SetInfo(o interface{}) {
    v := reflect.ValueOf(o)

    if v.Kind() != reflect.Struct {
        fmt.Println("not struct")
        return
    }

    //判断字段是否存在
    mv := v.MethodByName("GetInfo")
    if !mv.IsValid() {
        fmt.Println("wuxiao")
        return
    }

    args := []reflect.Value{reflect.ValueOf(o)} //初始化传入等参数,传入等类型只能是[]reflect.Value类型
    res := mv.Call(args)
    fmt.Println(res[0], res[1])

}

func main() {
    inputs := Person{"sbd", 12}
    SetInfo(inputs)
}

通过MethodByName先获取对象的Hello方法,然后准备要传入的参数,这里传入的参数必须是[]refelct.Value类型,传入的参数值必须强制转换为反射值类型refelct.Value
最后通过调用Call方法就可以实现通过反射”动态”调用对象的方法。

http://researchlab.github.io/2016/02/17/go-reflect-summarize/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容