文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何在go语言中利用反射精简代码

2023-06-05 04:16

关注

这篇文章主要为大家分析了如何在go语言中利用反射精简代码的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“如何在go语言中利用反射精简代码”的知识吧。

反射是 Go 语言中非常重要的一个知识点。反射是设计优雅程序的法宝,orm json 序列化,参数校验都离不开它,我们今天以一个业务开发中的实例,来简单讲解下反射在日常开发中的用处。

相信大家在使用 go 编写业务代码的时候都会写过这样的代码,这类代码的本质是,将一个集合中的数据按照名称绑定到结构体的对应属性上去,集合的类型不限于 map slice struct 甚至可以是 interface[^interface],

[^interface]: 这个就比较 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

这部分代码中最挫的地方就是结构体赋值的时候一个一个进行的复制,若整个结构体非常大,赋值的代码可能会写满满一屏,bug出现的几率也就大大增加,我们的目的就是通过反射来简化赋值的步骤,通过一个方法将集合中的数据绑定到结构体上

如何在go语言中利用反射精简代码

反射简述

要做到这一步,我们首先了解下,在 go 语言中,我们的变量是由什么组成的

图上第一个 type 是一个反射类型对象,表示了变量类型的一些信息,第二个表示结构体属性对应的的 type,包含了结构体属性的一些信息

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

如何在go语言中利用反射精简代码

bind function

看到这张图我们大概就明白应该怎样做了,目标是编写一个绑定方法,必须建立一个绑定关系,把这个结构体加上一个 tag ,通过 tag 和 map 中的数据建立关联

// 创建一个具有深刻含义的 tag    type TestValue struct {        IntValue     int            `qiudianzan:"int_value"`                StringValue string        `qiudianzan:"string_value"`        IntArray       []int        `qiudianzan:"int_array"`        StringArray []string    `qiudianzan:"string_array"`    }

紧接着获取结构体的 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)    }

绑定关系完成以后,就需要向结构体写入 map 中的值,这时候问题来了,map 中的数据结构都是 string ,但是结构体属性类型五花八门,并不能直接将 map 中的数据写入,还需要根据结构体属性类型做一步类型转换

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

通过反射可以获取属性的两种表示类型的反射对象

reflect.Type    // 静态类型    reflect.Kind    // 底层数据的类型

我们通过下面的例子来确定使用哪一个

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 的静态类型为 A,但是 a 的底层数据类型则为 struct,所以我们想根据类型解析,这里说的类型是指的 reflect.Kind

通过底层数据类型来转换 map 中的数据

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)

再稍稍整理下添加一点细节,一个简单的绑定函数就大功告成了

func bind(configMap map[string]string, result interface{}) error {    // 被绑定的结构体非指针错误返回    if reflect.ValueOf(result).Kind() != reflect.Ptr {        return errors.New("input not point")    }    // 被绑定的结构体指针为 null 错误返回    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        }        // 跳过结构体中不可 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}

之前的看起来非常难看的代码瞬间就变得很简单

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)

在这里只是提供一种绑定的思路,其实在实际开发中,遇到结构体值绑定/校验/格式化/方法绑定,都可以使用类似的思路,避免 one by one 的编写代码。

关于“如何在go语言中利用反射精简代码”就介绍到这了,更多相关内容可以搜索编程网以前的文章,希望能够帮助大家答疑解惑,请多多支持编程网网站!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯