真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼

這篇文章主要為大家分析了如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì)易懂,操作細(xì)節(jié)合理,具有一定參考價(jià)值。如果感興趣的話,不妨跟著跟隨小編一起來(lái)看看,下面跟著小編一起深入學(xué)習(xí)“如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼”的知識(shí)吧。

我們是自2013年創(chuàng)立以來(lái)的成都網(wǎng)站建設(shè)公司,提供網(wǎng)站建設(shè),電商網(wǎng)站設(shè)計(jì)開發(fā),外貿(mào)網(wǎng)站制作,響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì),重慶小程序開發(fā)、等服務(wù)。為客戶創(chuàng)造有價(jià)值的品牌營(yíng)銷體驗(yàn),讓互聯(lián)網(wǎng)提升企業(yè)的競(jìng)爭(zhēng)力!

反射是 Go 語(yǔ)言中非常重要的一個(gè)知識(shí)點(diǎn)。反射是設(shè)計(jì)優(yōu)雅程序的法寶,orm json 序列化,參數(shù)校驗(yàn)都離不開它,我們今天以一個(gè)業(yè)務(wù)開發(fā)中的實(shí)例,來(lái)簡(jiǎn)單講解下反射在日常開發(fā)中的用處。

相信大家在使用 go 編寫業(yè)務(wù)代碼的時(shí)候都會(huì)寫過這樣的代碼,這類代碼的本質(zhì)是,將一個(gè)集合中的數(shù)據(jù)按照名稱綁定到結(jié)構(gòu)體的對(duì)應(yīng)屬性上去,集合的類型不限于 map slice struct 甚至可以是 interface[^interface],

[^interface]: 這個(gè)就比較 trick 了

type TestValue struct {
        IntValue int
        StringValue string
        IntArray   []int
        StringArray []string
    }
    dataMap := map[string]string{
        "int_value":"1",
        "string_value":"str",
        "int_array":"[1,2,3]",
        "string_array":"[\"1\",\"2\",\"3\"]",
    }
    config := TestValue{}
    if value, ok := dataMap["int_value"]; ok {
        config.IntValue, _ = datautil.TransToInt64(value)
    }
    if value, ok := dataMap["string_value"]; ok {
        config.StringValue = value
    }
    if value, ok := dataMap["int_array"]; ok {
        config.IntArray = stringToIntArray(value)
    }
    if value, ok := dataMap["string_array"]; ok {
        config.StringArray = stringToStrArray(value)
    }
    return config

這部分代碼中最挫的地方就是結(jié)構(gòu)體賦值的時(shí)候一個(gè)一個(gè)進(jìn)行的復(fù)制,若整個(gè)結(jié)構(gòu)體非常大,賦值的代碼可能會(huì)寫滿滿一屏,bug出現(xiàn)的幾率也就大大增加,我們的目的就是通過反射來(lái)簡(jiǎn)化賦值的步驟,通過一個(gè)方法將集合中的數(shù)據(jù)綁定到結(jié)構(gòu)體上

如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼

反射簡(jiǎn)述

要做到這一步,我們首先了解下,在 go 語(yǔ)言中,我們的變量是由什么組成的

  • _type 類型信息

  • *data 指向?qū)嶋H值的指針

  • itab 接口方法

圖上第一個(gè) type 是一個(gè)反射類型對(duì)象,表示了變量類型的一些信息,第二個(gè)表示結(jié)構(gòu)體屬性對(duì)應(yīng)的的 type,包含了結(jié)構(gòu)體屬性的一些信息

reflect.Type : /go/src/reflect/value.go:36

如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼

bind function

看到這張圖我們大概就明白應(yīng)該怎樣做了,目標(biāo)是編寫一個(gè)綁定方法,必須建立一個(gè)綁定關(guān)系,把這個(gè)結(jié)構(gòu)體加上一個(gè) tag ,通過 tag 和 map 中的數(shù)據(jù)建立關(guān)聯(lián)

// 創(chuàng)建一個(gè)具有深刻含義的 tag
    type TestValue struct {
        IntValue     int            `qiudianzan:"int_value"`        
        StringValue string        `qiudianzan:"string_value"`
        IntArray       []int        `qiudianzan:"int_array"`
        StringArray []string    `qiudianzan:"string_array"`
    }

緊接著獲取結(jié)構(gòu)體的 tag ,通過反射輕而易舉的做到了

func bind(configMap map[string]string, result interface{}) error {
    v := reflect.ValueOf(result).Elem()
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        tag := t.Field(i).Tag.Get("qiudianzan")
        fmt.Println(tag)
    }

綁定關(guān)系完成以后,就需要向結(jié)構(gòu)體寫入 map 中的值,這時(shí)候問題來(lái)了,map 中的數(shù)據(jù)結(jié)構(gòu)都是 string ,但是結(jié)構(gòu)體屬性類型五花八門,并不能直接將 map 中的數(shù)據(jù)寫入,還需要根據(jù)結(jié)構(gòu)體屬性類型做一步類型轉(zhuǎn)換

v.Field(i).SetInt(res)
    v.Field(i).SetUint(res)
    v.Field(i).SetFloat(res)
    .
    .
    .

通過反射可以獲取屬性的兩種表示類型的反射對(duì)象

reflect.Type    // 靜態(tài)類型
    reflect.Kind    // 底層數(shù)據(jù)的類型

我們通過下面的例子來(lái)確定使用哪一個(gè)

type A struct {
    }
    func main() {
        var a A
        kinda := reflect.ValueOf(a).Kind()
        typea := reflect.TypeOf(a)
        fmt.Println(kinda)
        fmt.Println(typea)
    }
struct
    main.A

變量 a 的靜態(tài)類型為 A,但是 a 的底層數(shù)據(jù)類型則為 struct,所以我們想根據(jù)類型解析,這里說的類型是指的 reflect.Kind

通過底層數(shù)據(jù)類型來(lái)轉(zhuǎn)換 map 中的數(shù)據(jù)

switch v.Field(i).Kind() {
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
            res, err := strconv.ParseInt(value, 10, 64)
            if err != nil {
                return err
            }
            v.Field(i).SetInt(res)
        case reflect.String:
            v.Field(i).SetString(value)
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
            res, err := strconv.ParseUint(value, 10, 64)
            if err != nil {
                return err
            }
            v.Field(i).SetUint(res)

再稍稍整理下添加一點(diǎn)細(xì)節(jié),一個(gè)簡(jiǎn)單的綁定函數(shù)就大功告成了

func bind(configMap map[string]string, result interface{}) error {
    // 被綁定的結(jié)構(gòu)體非指針錯(cuò)誤返回
    if reflect.ValueOf(result).Kind() != reflect.Ptr {
        return errors.New("input not point")
    }
    // 被綁定的結(jié)構(gòu)體指針為 null 錯(cuò)誤返回
    if reflect.ValueOf(result).IsNil() {
        return errors.New("input is null")
    }
    v := reflect.ValueOf(result).Elem()
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        tag := t.Field(i).Tag.Get("json")
        // map 中沒該變量有則跳過
        value, ok := configMap[tag]
        if !ok {
            continue
        }
        // 跳過結(jié)構(gòu)體中不可 set 的私有變量
        if !v.Field(i).CanSet() {
            continue
        }
        switch v.Field(i).Kind() {
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
            res, err := strconv.ParseInt(value, 10, 64)
            if err != nil {
                return err
            }
            v.Field(i).SetInt(res)
        case reflect.String:
            v.Field(i).SetString(value)
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
            res, err := strconv.ParseUint(value, 10, 64)
            if err != nil {
                return err
            }
            v.Field(i).SetUint(res)
        case reflect.Float32:
            res, err := strconv.ParseFloat(value, 32)
            if err != nil {
                return err
            }
            v.Field(i).SetFloat(res)
        case reflect.Float64:
            res, err := strconv.ParseFloat(value, 64)
            if err != nil {
                return err
            }
            v.Field(i).SetFloat(res)
        case reflect.Slice:
            var strArray []string
            var valArray []reflect.Value
            var valArr reflect.Value
            elemKind := t.Field(i).Type.Elem().Kind()
            elemType := t.Field(i).Type.Elem()
            value = strings.Trim(strings.Trim(value, "["), "]")
            strArray = strings.Split(value, ",")
            switch elemKind {
            case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                for _, e := range strArray {
                    ee, err := strconv.ParseInt(e, 10, 64)
                    if err != nil {
                        return err
                    }
                    valArray = append(valArray, reflect.ValueOf(ee).Convert(elemType))
                }
            case reflect.String:
                for _, e := range strArray {
                    valArray = append(valArray, reflect.ValueOf(strings.Trim(e, "\"")).Convert(elemType))
                }
            }
            valArr = reflect.Append(v.Field(i), valArray...)
            v.Field(i).Set(valArr)
        }
    }
    return nil
}

之前的看起來(lái)非常難看的代碼瞬間就變得很簡(jiǎn)單

type TestValue struct {
        IntValue int
        StringValue string
        IntArray   []int
        StringArray []string
    }
    dataMap := map[string]string{
        "int_value":"1",
        "string_value":"str",
        "int_array":"[1,2,3]",
        "string_array":"[\"1\",\"2\",\"3\"]",
    }
    config := TestValue{}
    err := bind(dataMap,&config)

在這里只是提供一種綁定的思路,其實(shí)在實(shí)際開發(fā)中,遇到結(jié)構(gòu)體值綁定/校驗(yàn)/格式化/方法綁定,都可以使用類似的思路,避免 one by one 的編寫代碼。

關(guān)于“如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼”就介紹到這了,更多相關(guān)內(nèi)容可以搜索創(chuàng)新互聯(lián)以前的文章,希望能夠幫助大家答疑解惑,請(qǐng)多多支持創(chuàng)新互聯(lián)網(wǎng)站!


網(wǎng)頁(yè)標(biāo)題:如何在go語(yǔ)言中利用反射精簡(jiǎn)代碼
網(wǎng)站URL:http://weahome.cn/article/pseeis.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部