我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

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

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

这篇文章主要为大家分析了如何在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 类型信息

  • *data 指向实际值的指针

  • itab 接口方法

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

reflect.Type : /go/class="lazy" data-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

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

利用反射机制优化Go语言代码

利用反射机制优化 go 语言代码可以:获取类型信息(名称、类型、字段、方法):reflect.typeof()修改值(结构体、切片):reflect.valueof().elem().fieldbyname().set()遍历结构体字段:r
利用反射机制优化Go语言代码
2024-04-08

详解如何在Go语言中调用C源代码

目录开坑说明内嵌形式外置的C代码1. 构建libauth.a静态库2. 对main.go稍加修改3. 编译开坑说明 最近在编写客户端程序或与其他部门做功能集成时多次碰到了跨语言的sdk集成,虽说方案很多诸如rpc啊,管道啊,文件io啊,un
2022-06-07

泛型函数如何在Go语言中提高代码可读性?

泛型函数允许 go 代码中编写处理多种类型的函数,提高可读性。泛型函数使用尖括号 表示泛型类型参数。泛型函数的行为基于其参数的类型。泛型函数消除针对不同类型的重复代码,提高代码的可读性。使用泛型函数时无需针对每种类型编写定制代码,减少复制
泛型函数如何在Go语言中提高代码可读性?
2024-04-16

详解Go语言如何利用高阶函数写出优雅的代码

高阶函数(Hiher-order Function)定义为:满足下列条件之一的函数:接收一个或多个函数作为参数;返回值是一个函数。本文为大家介绍了如何利用高阶函数写出优雅的代码,希望对大家有所帮助
2023-01-05

如何利用MySQL和Go语言开发一个简单的在线邮件系统

如何利用MySQL和Go语言开发一个简单的在线邮件系统在当今高度数字化的世界中,电子邮件已成为人们日常沟通的重要方式。对于一个在线邮件系统的开发,数据库的选择是至关重要的。MySQL作为一个开源且稳定的关系型数据库,与Go语言搭配使用,可以
2023-10-22

如何利用MySQL和Go语言开发一个简单的在线投资平台

如何利用MySQL和Go语言开发一个简单的在线投资平台简介:作为一种数字化的金融服务,在线投资平台的发展正日益受到人们的关注。本文将介绍如何利用MySQL和Go语言开发一个简单的在线投资平台,包括数据库设计和相关代码示例。数据库设计:首先,
2023-10-22

如何利用MySQL和Go语言开发一个简单的在线票务预订系统

如何利用MySQL和Go语言开发一个简单的在线票务预订系统引言:随着互联网的发展,人们越来越喜欢在线预订各种服务,包括机票、酒店、电影票等。为了满足用户的需求,开发一个简单的在线票务预订系统非常有必要。本文将介绍如何利用MySQL和Go语言
2023-10-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录