reflect使用

reflect概述

反射是一種檢查存儲在 接口變量中的<值,類型>對 的機制窄绒,借助go反射包提供的reflect.TypeOfreflect.ValueOf可以方便的訪問到一個接口值的reflect.Typereflect.Value部分港准,從而可進一步得到 這個接口的結(jié)構(gòu)類型和對其進行值的修改操作

反射的使用

  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()得到對應(yīng)的value

    for i := 0; i < t.NumField(); i++ { //NumField取出這個接口所有的字段數(shù)量
        f := t.Field(i)                                   //取得結(jié)構(gòu)體的第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()循環(huán)獲取接口對象所有方法的名稱和類型。
  1. 反射接口對象中的匿名或嵌入字段信息
    先再添加一個Manager結(jié)構(gòu),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結(jié)構(gòu)體中的Id字段茎截。
2.通過FieldByName("Id")也可以取得User結(jié)構(gòu)體中Id字段。

  1. 通過反射修改對象
    上面通過reflect.TypeOfreflect.ValueOf已經(jīng)可以得到接口對象的類型,字段和方法等屬性了赶盔,怎么通過反射來修改對象的字段值企锌?
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變量的地址值類型之后先調(diào)用Elem()返回地址指針指向的值的Value封裝撕攒。然后通過Set方法進行修改賦值陡鹃。

通過反射可以很容易的修改變量的值,怎么來修改結(jié)構(gòu)體中的字段值抖坪?

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
    }

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

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

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

要成功修改結(jié)構(gòu)體中的某個字段,主要進行以下操作:

  1. 首先要反射出這個字段的地址值類型;
  2. 判斷反射返回類型是否為reflect.Ptr指針類型(通過指針才能操作對象地址中的值)同時還要判斷這個元素是否可以修改;
  3. 通過FieldByName的返回值判斷字段是否存在
  4. 通過Kind()和Set來修改字段的值
  1. 通過反射“動態(tài)”調(diào)用方法
    現(xiàn)在已經(jīng)可以通過反射獲取并修改接口對象的字段萍鲸,類型等信息了,那怎么通過反射“動態(tài)”調(diào)用接口對象等方法擦俐?
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)} //初始化傳入等參數(shù)脊阴,傳入等類型只能是[]reflect.Value類型
    res := mv.Call(args)
    fmt.Println(res[0], res[1])

}

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

通過MethodByName先獲取對象的Hello方法,然后準備要傳入的參數(shù),這里傳入的參數(shù)必須是[]refelct.Value類型,傳入的參數(shù)值必須強制轉(zhuǎn)換為反射值類型refelct.Value
最后通過調(diào)用Call方法就可以實現(xiàn)通過反射”動態(tài)”調(diào)用對象的方法蚯瞧。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘿期,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子埋合,更是在濱河造成了極大的恐慌备徐,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甚颂,死亡現(xiàn)場離奇詭異检柬,居然都是意外死亡五芝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門胃夏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诸衔,“玉大人撩幽,你說我怎么就攤上這事拆撼【迪ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵禽绪,是天一觀的道長。 經(jīng)常有香客問我洪规,道長印屁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任斩例,我火速辦了婚禮雄人,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘念赶。我一直安慰自己础钠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布叉谜。 她就那樣靜靜地躺著旗吁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪停局。 梳的紋絲不亂的頭發(fā)上很钓,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天香府,我揣著相機與錄音,去河邊找鬼码倦。 笑死企孩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袁稽。 我是一名探鬼主播勿璃,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼推汽!你這毒婦竟也來了补疑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤民泵,失蹤者是張志新(化名)和其女友劉穎癣丧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栈妆,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胁编,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳞尔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嬉橙。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寥假,靈堂內(nèi)的尸體忽然破棺而出市框,到底是詐尸還是另有隱情,我是刑警寧澤糕韧,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布枫振,位于F島的核電站,受9級特大地震影響萤彩,放射性物質(zhì)發(fā)生泄漏粪滤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一雀扶、第九天 我趴在偏房一處隱蔽的房頂上張望杖小。 院中可真熱鬧,春花似錦愚墓、人聲如沸予权。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扫腺。三九已至,卻和暖如春议经,著一層夾襖步出監(jiān)牢的瞬間斧账,已是汗流浹背谴返。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咧织,地道東北人嗓袱。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像习绢,于是被迫代替她去往敵國和親渠抹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容