go基礎(chǔ)——反射二(反射使用)

內(nèi)容

1 獲取接口類型值
2 修改接口類型值
3 反射調(diào)用函數(shù)
4 反射調(diào)用方法
5 reflect包api使用

先回顧一下反射三大定律:

1 反射可以將“接口類型變量”轉(zhuǎn)換為“反射類型對象”;

2 反射可以將“反射類型對象”轉(zhuǎn)換為“接口類型變量”;

3 如果要修改“反射類型對象”腾供,其值必須是“可寫的”(settable)。

一 反射獲取接口類型值

通過接口變量獲取變量指向的值和類型信息:
1 先通過接口變量獲取value反射對象
2 通過value的Interface可以獲取接口變量指向的對象暑始,是interface類型
3 斷言獲取接口值
4 通過value對象type()方法獲取接口變量的類型信息,是靜態(tài)類型信息
5 通過type對象獲取接口變量的種類信息烫止,是具體的基本類型信息蒋荚,如:int slice func map string等基礎(chǔ)類型,在reflect/type有所有的基礎(chǔ)類型kind

type MyType int
func main() {
    var i = MyType(1)
    iValue := reflect.ValueOf(i)
    iType := reflect.TypeOf(i)

    fmt.Printf(" i value: %v", iValue.Interface()) // i value: 1
    fmt.Printf(" i type: %v", iValue.Type()) // i type: main.MyType
    fmt.Printf(" i kind: %v", iType.Kind())  //  i kind: int

}

// 獲取結(jié)構(gòu)體字段信息
// 結(jié)構(gòu)體包含不可導(dǎo)出字段s馆蠕,此時反射獲取這個不可導(dǎo)出字段時期升,會panic
func reflectGetStructField() {
    m := MyStruct{
        A: 1,
        B: true,
        s: "test",
    }
    mValue := reflect.ValueOf(m)
    mType := reflect.TypeOf(m)
    for i:=0;  i < mValue.NumField(); i++ {
        v := mValue.Field(i)
        fmt.Printf(" field: %v {type: %v; value:%v}", mType.Field(i).Name,
            v.Type(), v.Interface())
    }
}
//panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
// 將s調(diào)整為S
輸出:
 field: A {type: int; value:1} field: B {type: bool; value: true} field: S {type: string; value: test}

二 修改接口類型值

要修改接口類型值,要謹(jǐn)記反射第三定律互躬,其值要是可寫的
總結(jié)為:
1 根據(jù)接口指針變量獲取value對象播赁;
2 根據(jù)Elem方法value變量具體指向的數(shù)據(jù);
3 可以根據(jù)value對象的CanSet方法判斷是否可寫吼渡;
4 調(diào)用value對象的SetX方法進(jìn)行改變容为;

func reflectModifyStructField() {
    m := MyStruct{
        A: 1,
        B: true,
        S: "test",
    }

    mValue := reflect.ValueOf(&m).Elem()
    // 以下兩種都是不行的,獲取接口指針變量的value對象后寺酪,一定要調(diào)用Elem()方法解引用獲取指向的具體數(shù)據(jù)坎背,
    // 因為我們對具體數(shù)據(jù)域修改,不是對mValue這個value 類型變量修改寄雀;
    // 1 mValue := reflect.ValueOf(&m)
    // 2 mValue := reflect.ValueOf(m)
    mType := reflect.TypeOf(&m)
    fmt.Println(fmt.Sprintf("%v can set: %v", mType, mValue.CanSet()))
    for i:=0;  i < mValue.NumField(); i++ {
        if mValue.Field(i).Kind() == reflect.String {
            mValue.Field(i).SetString("new test")
        }
    }
    fmt.Println(fmt.Sprintf("%v : %v", mType, m))
}
// 輸出:
*main.MyStruct can set: true
*main.MyStruct : {1 true new test}

三 反射調(diào)用函數(shù)

1 獲取方法對象變量
2 獲取value反射對象
3 構(gòu)造方法入?yún)lice, reflect.Value切片
4 調(diào)用value Call方法
5 得到執(zhí)行結(jié)果得滤,是value 切片

func Add(i, j int) int {
    return i + j
}

func main() {
    a := Add
    aValue := reflect.ValueOf(a)
    aParam := make([]reflect.Value, 2)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)
    fmt.Println("reflect exe add, result: ", aValue.Call(aParam)[0])
}

// 輸出
reflect exe add, result:  3

四 反射調(diào)用方法

1 獲取value對象
2 構(gòu)造方法入?yún)lice, reflect.Value切片
3 根據(jù)MethodByName()獲取方法value對象
4 調(diào)用call方法執(zhí)行

type MyStruct struct {
    A int
    B bool
    S string
}
func (m *MyStruct) Subtract(i, j int) int {
    return i - j
}
func main() {
    m := MyStruct{}

    aParam := make([]reflect.Value, 2)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)

    //方法是指針接收器,這里必須是指針變量 
    mValue := reflect.ValueOf(&m)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)
    fmt.Println("reflect MyStruct Subtract, result: ", mValue.MethodByName("Subtract").Call(aParam)[0])

}
// 輸出
reflect MyStruct Subtract, result:  -1

五 reflect包api使用

1 創(chuàng)建slice map chan

  // 反射創(chuàng)建map slice channel
    intSlice := make([]int, 0)
    mapStringInt := make(map[string]int)
    sliceType := reflect.TypeOf(intSlice)
    mapType := reflect.TypeOf(mapStringInt)

    // 創(chuàng)建新值
    intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
    mapReflect := reflect.MakeMap(mapType)

    // 使用新創(chuàng)建的變量
    v := 10
    rv := reflect.ValueOf(v)
    intSliceReflect = reflect.Append(intSliceReflect, rv)
    intSlice2 := intSliceReflect.Interface().([]int)
    fmt.Println(intSlice2)

    k := "hello"
    rk := reflect.ValueOf(k)
    mapReflect.SetMapIndex(rk, rv)
    mapStringInt2 := mapReflect.Interface().(map[string]int)

2 創(chuàng)建函數(shù)
使用reflect.Makefunc()創(chuàng)建函數(shù)盒犹,入?yún)⑹牵合胍獎?chuàng)建的函數(shù)的reflect.type和一個輸入?yún)?shù)是[] reflect.value類型的slice懂更,其輸出參數(shù)也是類型reflect.value 切片的閉包

package main

import (
    "reflect"
    "time"
    "fmt"
    "runtime"
)
/*
將創(chuàng)建Func封裝眨业, 非reflect.Func類型會panic
當(dāng)然makeFunc的閉包函數(shù)表達(dá)式類型是固定的,可以查閱一下文檔沮协。
細(xì)讀文檔的reflect.Value.Call()方法龄捡。
 */
func MakeTimedFunction(f interface{}) interface{} {
    rf := reflect.TypeOf(f)
    if rf.Kind() != reflect.Func {
        panic("非Reflect.Func")
    }
    vf := reflect.ValueOf(f)
    wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
        start := time.Now()
        out := vf.Call(in)
        end := time.Now()
        fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
        return out
    })
    return wrapperF.Interface()
}

func time1() {
    fmt.Println("time1Func===starting")
    time.Sleep(1 * time.Second)
    fmt.Println("time1Func===ending")
}

func time2(a int) int {
    fmt.Println("time2Func===starting")
    time.Sleep(time.Duration(a) * time.Second)
    result := a * 2
    fmt.Println("time2Func===ending")
    return result
}

func main() {
    timed := MakeTimedFunction(time1).(func())
    timed()
    timedToo := MakeTimedFunction(time2).(func(int) int)
    time2Val := timedToo(5)
    fmt.Println(time2Val)
}

引用文檔:Golang Reflect反射的使用詳解1 https://my.oschina.net/90design/blog/1614820

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市慷暂,隨后出現(xiàn)的幾起案子聘殖,更是在濱河造成了極大的恐慌,老刑警劉巖呜呐,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件就斤,死亡現(xiàn)場離奇詭異悍募,居然都是意外死亡蘑辑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門坠宴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洋魂,“玉大人,你說我怎么就攤上這事喜鼓「笨常” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵庄岖,是天一觀的道長豁翎。 經(jīng)常有香客問我,道長隅忿,這世上最難降的妖魔是什么心剥? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮背桐,結(jié)果婚禮上优烧,老公的妹妹穿的比我還像新娘。我一直安慰自己链峭,他們只是感情好畦娄,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弊仪,像睡著了一般熙卡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上励饵,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天驳癌,我揣著相機(jī)與錄音,去河邊找鬼曲横。 笑死喂柒,一個胖子當(dāng)著我的面吹牛不瓶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灾杰,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蚊丐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了艳吠?” 一聲冷哼從身側(cè)響起麦备,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昭娩,沒想到半個月后凛篙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡栏渺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年呛梆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片磕诊。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡填物,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霎终,到底是詐尸還是另有隱情滞磺,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布莱褒,位于F島的核電站击困,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏广凸。R本人自食惡果不足惜阅茶,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炮障。 院中可真熱鬧目派,春花似錦、人聲如沸胁赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽智末。三九已至谅摄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間系馆,已是汗流浹背送漠。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留由蘑,地道東北人闽寡。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓代兵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爷狈。 傳聞我的和親對象是個殘疾皇子植影,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356