欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang反射模塊reflect使用方式示例詳解

 更新時間:2023年01月04日 14:21:55   作者:utmhikari  
Golang的反射功能,在很多場景都會用到,最基礎(chǔ)的莫過于rpc、orm跟json的編解碼,更復(fù)雜的可能會到做另外一門語言的虛擬機,這篇文章主要介紹了Golang反射模塊reflect使用方式探索,需要的朋友可以參考下

Golang的反射功能,在很多場景都會用到,最基礎(chǔ)的莫過于rpc、ormjson的編解碼,更復(fù)雜的可能會到做另外一門語言的虛擬機。通過反射模塊,我們可以在編程語言的runtime運行時期間去訪問內(nèi)部產(chǎn)生對象的信息。了解反射模塊的實現(xiàn),對我們了解Golang對象機制本身,也是莫大的幫助。

今天,恰逢陽康+新年,就決定來探究一下Golang的反射模塊——reflect。

從最基礎(chǔ)的開始,reflect模塊,以獲取整數(shù)對象的類型信息為例,我們可以這么用:

func TestReflect_Integer(t *testing.T) {
    i := 1
    v := reflect.ValueOf(i)
    vKind := v.Kind()
    vType := v.Type()
    t.Logf("i kind: %+v\n", vKind)
    t.Logf("i type: %+v\n", vType)

    itf := v.Interface()
    j, ok := itf.(int)
    t.Logf("j val: %+v\n", j)
    if !ok || j != i {
        t.Fatalf("i != j")
    }
}

reflect.ValueOf的入?yún)⑹?code>interface{}類型,新版本也叫做any,得到的返回值是reflect.Value類型的對象,可以認為是對原對象的一個描述性質(zhì)的對象(元對象,233)。reflect.Value包含幾個成員:

  • typ:原對象類型的元信息
  • ptr:指向原對象值的原生指針
  • flag:原對象類型的正整數(shù)標識

當(dāng)調(diào)用KindType接口,四舍五入就是獲取了reflect.Value對象的flagtyp成員實例了。

要把reflect.Value轉(zhuǎn)換回原對象,首先需要通過Interface方法轉(zhuǎn)化成interface{}類型的對象,再通過.(int)強轉(zhuǎn)邏輯去轉(zhuǎn)化成原對象。但這里需要注意下,如果真需要用到reflect反射功能且涉及到一些看似要“強轉(zhuǎn)”的場景,可能是沒有必要真的在代碼中強轉(zhuǎn)回特定類型對象的。好比rpc的調(diào)用,實質(zhì)是在聲明接口方法的基礎(chǔ)上,把接口方法變成reflect.Value對象,再用Func.Call方法做函數(shù)調(diào)用。這里,也給個example

假設(shè)我們定義User結(jié)構(gòu)體,內(nèi)部嵌套UserInfo結(jié)構(gòu)體,均有json標注,然后定義了ToStringResponse兩個方法,大概長這樣:

type UserInfo struct {
    Desc string `json:"desc"`
    City string `json:"city" desc:"the city of user"`
}

type User struct {
    ID   int       `json:"id"`
    Name string    `json:"name"`
    Info *UserInfo `json:"info"`
}

func (u *User) ToString() string {
    return fmt.Sprintf("[%d]%s", u.ID, u.Name)
}

func (u *User) Response(from string, msg string) string {
    return fmt.Sprintf("User %s received msg %s from <%s>", u.ToString(), msg, from)
}

那么比如Response方法,實際上也能夠這樣調(diào)用:

func TestReflect_Method(t *testing.T) {
    u := &User{1, "jack", nil}
    uPtr := reflect.ValueOf(u)
    // MethodByName:獲取特定名字的Method
    meth, ok := uPtr.Type().MethodByName("Response")
    if !ok {
        t.Fatalf("no method named Response")
    }
    t.Logf("meth Response: %+v\n", meth)

    methType := meth.Type
    // 入?yún)?個:User實例、from、msg
    if methType.NumIn() != 3 {
        t.Fatalf("invalid NumIn %d, expected %d", methType.NumIn(), 3)
    }
    // 返回值1個:response string
    if methType.NumOut() != 1 {
        t.Fatalf("invalid NumOut %d, expected %d", methType.NumOut(), 1)
    }
    // 通過Func.Call得到返回值list
    from, msg := reflect.ValueOf("client"), reflect.ValueOf("ping")
    rets := meth.Func.Call([]reflect.Value{uPtr, from, msg})
    if len(rets) != 1 {
        t.Fatalf("invalid num rets %d, expected %d", len(rets), 1)
    }
    // 返回1個string對象
    respVal := rets[0]
    if respVal.Type() != reflect.TypeOf("") {
        t.Fatalf("invalid ret type %v, expected %s", respVal.Type(), "STRING")
    }
    resp, ok := respVal.Interface().(string)
    if !ok {
        t.Fatalf("ret value cannot be converted to string")
    }
    t.Logf("resp: %s\n", resp)
}

通過MethodByName方法,可以定位到一個對象下的某個名字的方法實例,通過對方法實例調(diào)用Func.Call,就能實際實現(xiàn)對方法的調(diào)用,得到返回值列表。

涉及到指針對象的反射值,可以通過reflect.Indirect(反射值)或者反射值.Elem()的方式,獲取到指針指向?qū)嵗姆瓷渲怠?code>example代碼如下,因為上面我們定義ToStringResponse方法綁定的是指針對象,在這樣的條件下,指針指向?qū)嵗姆瓷渲稻湍貌坏?code>ToString方法了,打印便知:

func TestReflect_Pointer(t *testing.T) {
    u := &User{1, "jack", nil}
    vPtr := reflect.ValueOf(u)
    vPtrKind := vPtr.Kind()
    vPtrType := vPtr.Type()
    t.Logf("ptr kind: %+v\n", vPtrKind)
    t.Logf("ptr type: %+v\n", vPtrType)
    meth, ok := vPtrType.MethodByName("ToString")
    t.Logf("ptr meth ToString: %+v (%+v)\n", meth, ok)

    vVal := reflect.Indirect(vPtr)
    // vVal := vPtr.Elem()
    vValKind := vVal.Kind()
    vValType := vVal.Type()
    t.Logf("val kind: %+v\n", vValKind)
    t.Logf("val type: %+v\n", vValType)
    meth, ok = vValType.MethodByName("ToString")
    t.Logf("val meth ToString: %+v (%+v)\n", meth, ok)
}

再進一步來看,對于slice、map這類對象,也可以通過reflect.Value內(nèi)置的一些方法,訪問到內(nèi)部的對象。假設(shè)我們要實現(xiàn)slicemap的復(fù)制操作,用純反射的方式也可以實現(xiàn):

func TestReflect_CopySliceAndMap(t *testing.T) {
    mp := map[string]int{
        "jack": 1,
        "tom":  2,
    }
    sl := []int{1, 1, 2, 3, 5, 8}
    vals := []reflect.Value{reflect.ValueOf(mp), reflect.ValueOf(sl)}
    var copyVals []reflect.Value

    for _, val := range vals {
        var copyVal reflect.Value
        switch val.Kind() {
        case reflect.Map:
            // MakeMap:創(chuàng)建map實例
            copyVal = reflect.MakeMap(val.Type())
            // MapRange:獲取map對象的Iterator
            iter := val.MapRange()
            for iter.Next() {
                copyVal.SetMapIndex(iter.Key(), iter.Value())
            }
        case reflect.Slice:
            // AppendSlice:在一個slice的基礎(chǔ)上extend另一個slice
            copyVal = reflect.AppendSlice(
                reflect.MakeSlice(val.Type(), 0, val.Len()),
                val)
        }
        copyVals = append(copyVals, copyVal)
    }

    // 通過DeepEqual方法,可以做值的相等性比較
    for _, val := range copyVals {
        switch val.Kind() {
        case reflect.Map:
            if val.Len() != len(mp) {
                t.Fatalf("invalid map length %d, expected %d", val.Len(), len(mp))
            }
            copyVal, ok := val.Interface().(map[string]int)
            if !ok {
                t.Fatalf("map convert failed")
            }
            t.Logf("copied map: %+v", copyVal)
            for k, v := range mp {
                copyV, ok := copyVal[k]
                if !ok || !reflect.DeepEqual(v, copyV) {
                    t.Fatalf("copy value of key %s failed, expected %d, actual %d", k, v, copyV)
                }
            }
        case reflect.Slice:
            if val.Len() != len(sl) {
                t.Fatalf("invalid slice length %d, expected %d", val.Len(), len(sl))
            }
            copyVal, ok := val.Interface().([]int)
            if !ok {
                t.Fatalf("slice convert failed")
            }
            t.Logf("copied slice: %+v", copyVal)
            if !reflect.DeepEqual(copyVal, sl) {
                t.Fatalf("slice not equal")
            }
        }
    }
}

最后,也看一下reflect作用到struct定義的一些使用方法,這里就需要看reflect.Value.Type()返回的Type實例有什么功能了。對于Type實例來講,我們可以遍歷所有結(jié)構(gòu)體字段定義,甚至是訪問標注信息,比如json、orm的編解碼,就極度依賴這些字段的標注信息。這里的實現(xiàn),也給個example

func TestReflect_Struct(t *testing.T) {
    st := reflect.ValueOf(&User{
        ID:   9,
        Name: "Ronaldo",
        Info: &UserInfo{
            Desc: "SC",
            City: "Madrid",
        },
    }).Elem().Type()
    // 多少個字段
    numField := st.NumField()
    t.Logf("num fields: %d", numField)
    // 一個個字段遍歷,輸出字段名字、數(shù)據(jù)類型、json標注
    for i := 0; i < numField; i++ {
        field := st.Field(i)
        t.Logf("field %d -> name: %s, type: %v, json: %s",
            i+1,
            field.Name,
            field.Type,
            field.Tag.Get("json"))
    }
    // 嵌套的字段,用FieldByIndex可以定位到
    cityField := st.FieldByIndex([]int{2, 1})
    // 按照上面UserInfo.City的定義,拿取desc標注信息
    cityFieldDesc, ok := cityField.Tag.Lookup("desc")
    if !ok {
        t.Fatalf("cannot find city field desc")
    }
    t.Logf("CityField -> name: %s, type: %v, desc: %s",
        cityField.Name,
        cityField.Type,
        cityFieldDesc)
}

可以看到,我們可以很方便拿到結(jié)構(gòu)體的定義信息,更加深層嵌套的定義信息也都能拿到??梢哉f,太靈活了!

到此這篇關(guān)于Golang反射模塊reflect使用方式探索的文章就介紹到這了,更多相關(guān)Golang反射模塊reflect內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言開發(fā)代碼自測絕佳go?fuzzing用法詳解

    Go語言開發(fā)代碼自測絕佳go?fuzzing用法詳解

    這篇文章主要為大家介紹了Go語言開發(fā)代碼自測絕佳go?fuzzing用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Golang錯誤處理方式異常與error

    Golang錯誤處理方式異常與error

    我們在使用Golang時,不可避免會遇到異常情況的處理,與Java、Python等語言不同的是,Go中并沒有try...catch...這樣的語句塊,這個時候我們?nèi)绾尾拍芨玫奶幚懋惓D??本文來教你正確方法
    2023-01-01
  • Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享

    Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享

    這篇文章主要為大家詳細介紹了如何使用Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù),文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下
    2023-05-05
  • Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說明

    Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說明

    這篇文章主要介紹了Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go JSON編碼與解碼的實現(xiàn)

    Go JSON編碼與解碼的實現(xiàn)

    這篇文章主要介紹了Go JSON編碼與解碼的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • go第三方庫sqlx操作MySQL及ORM原理

    go第三方庫sqlx操作MySQL及ORM原理

    這篇文章主要為大家介紹了go第三方庫sqlx操作MySQL及ORM實現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • go語言中的Carbon庫時間處理技巧

    go語言中的Carbon庫時間處理技巧

    這篇文章主要介紹了go語言中的Carbon庫時間處理,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • 深入解析Sync.Pool如何提升Go程序性能

    深入解析Sync.Pool如何提升Go程序性能

    在并發(fā)編程中,資源的分配和回收是一個很重要的問題。Go?語言的?Sync.Pool?是一個可以幫助我們優(yōu)化這個問題的工具。本篇文章將會介紹?Sync.Pool?的用法、原理以及如何在項目中正確使用它,希望對大家有所幫助
    2023-05-05
  • 深度剖析Golang如何實現(xiàn)GC掃描對象

    深度剖析Golang如何實現(xiàn)GC掃描對象

    這篇文章主要為大家詳細介紹了Golang是如何實現(xiàn)GC掃描對象的,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,需要的小伙伴可以參考一下
    2023-03-03
  • go語言搬磚之go jmespath實現(xiàn)查詢json數(shù)據(jù)

    go語言搬磚之go jmespath實現(xiàn)查詢json數(shù)據(jù)

    這篇文章主要為大家介紹了go語言搬磚之go jmespath實現(xiàn)查詢json數(shù)據(jù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06

最新評論