Go语言通过反射获取各种类型变量的值

Go语言通过反射获取各种类型变量的值

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。

1、什么是反射

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。

Golang 反射包中有两对非常重要的函数和类型,两个函数分别是:

  • reflect.TypeOf 能获取类型信息 reflect.Type;

  • reflect.ValueOf 能获取数据的运行时表示 reflect.Value;

2、reflect.Type

Golang 是一门静态类型的语言,反射是建立在类型之上的。

通过 reflect.TypeOf() 函数可以获得任意值的类型信息。

2.1 类型Type和种类Kind

诸如 int32, slice, map 以及通过 type 关键词自定义的类型。

种类 Kind 可以理解为类型的具体分类,如 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种

类。

使用 reflect.TypeOf() 获取变量类型以及种类。

package mainimport ("fmt""reflect"
)func main() {type MyInt32 int32a := MyInt32(1)b := int32(1)// reflect.TypeOf(a):main.MyInt32 Kind:int32fmt.Printf("reflect.TypeOf(a):%v Kind:%v\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())// reflect.TypeOf(b):int32 Kind:int32fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", reflect.TypeOf(b), reflect.TypeOf(b).Kind())
}

从代码输出可以看出 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种类。

种类定义点击查看:

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uintconst (Invalid Kind = iotaBoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128ArrayChanFuncInterfaceMapPointerSliceStringStructUnsafePointer
)

2.2 引用指向元素的类型

// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
Elem() Type

部分情况我们需要获取指针指向元素的类型、或者 slice 元素的类型,可以 reflect.Elem() 函数获取。

package mainimport ("fmt""reflect"
)type myStruct struct {
}func main() {a := &myStruct{}typeA := reflect.TypeOf(a)// TypeOf(a):*main.myStruct Kind:ptrfmt.Printf("TypeOf(a):%v Kind:%v\n", typeA, typeA.Kind())// TypeOf(a).Elem():main.myStruct Elem().Kind:structfmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\n", typeA.Elem(), typeA.Elem().Kind())s := []int64{}typeS := reflect.TypeOf(s)// TypeOf(s):[]int64 Kind:slicefmt.Printf("TypeOf(s):%v Kind:%v\n", typeS, typeS.Kind())// TypeOf(s).Elem():int64 Elem().Kind:int64fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\n", typeS.Elem(), typeS.Elem().Kind())
}

从代码输出可以看出,通过 reflect.Elem() 函数可以获取引用指向数据的类型。

2.3 结构体成员类型

通过 NumField 获取成员数量,Field 通过下标访问成员的类型信息 StructField,包括成员名称、类型、Tag 信息

等。

package mainimport ("fmt""reflect"
)type secStruct struct {Cnt []int64
}type myStruct struct {Num   int    `json:"num_json" orm:"column:num_orm"`Desc  string `json:"desc_json" orm:"column:desc_orm"`Child secStruct
}func main() {s := myStruct{}typeS := reflect.TypeOf(s)// 成员数量// NumField:3fmt.Printf("NumField:%v \n", typeS.NumField())// 每个成员的信息 包括名称、类型、Tagfor i := 0; i < typeS.NumField(); i++ {// 通过下标访问成员// Field(0):{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}// Field(1):{Name:Desc PkgPath: Type:string Tag:json:"desc_json" orm:"column:desc_orm" Offset:8 Index:[1] Anonymous:false}// Field(2):{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}fmt.Printf("Field(%v):%+v\n", i, typeS.Field(i))}// 通过名称访问成员field, ok := typeS.FieldByName("Num")// FieldByName("Num") ok:true field:{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}fmt.Printf("FieldByName(\"Num\") ok:%v field:%+v\n", ok, field)// 获取tag值// json tag val:num_jsonfmt.Printf("json tag val:%+v\n", field.Tag.Get("json"))if value, ok := field.Tag.Lookup("orm"); ok {// rm tag val:column:num_ormfmt.Printf("orm tag val:%+v\n", value)}// 获取嵌套结构体的字段// Cnt field:{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2}))// Cnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false}fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2, 0}))
}

3、reflect.Value

通过 reflect.ValueOf 获取变量值、值类型,种类为 Array, Chan, Map, Slice 或 String,可通过 Len() 获取长度。

package mainimport ("fmt""reflect"
)func main() {b := int32(1)valueB := reflect.ValueOf(b)// reflect.TypeOf(b):1 Kind:int32fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", valueB, valueB.Kind())s := "abcdefg"valueS := reflect.ValueOf(s)// reflect.TypeOf(s):abcdefg Kind:string Len:7fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
}

3.1 结构体的成员的值

和 2.3 结构体成员类型获取结构体成员类型类似,reflect 提供了 NumField 获取成员数量,Field 通过下标访问成

员的值。

package mainimport ("fmt""reflect"
)type secStruct struct {Cnt []int64
}type myStruct struct {Num   int    `json:"num_json" orm:"column:num_orm"`Desc  string `json:"desc_json" orm:"column:desc_orm"`Child secStruct
}func main() {s := myStruct{Num:   100,Desc:  "desc",Child: secStruct{[]int64{1, 2, 3}},}valueS := reflect.ValueOf(s)// 成员数量// NumField:3fmt.Printf("NumField:%v \n", valueS.NumField())// 每个成员的值for i := 0; i < valueS.NumField(); i++ {// 通过下标访问成员// value(0):100// value(1):desc// value(2):{Cnt:[1 2 3]}fmt.Printf("value(%v):%+v\n", i, valueS.Field(i))}// 通过名称访问成员value := valueS.FieldByName("Num")// FieldByName("Num") value:100fmt.Printf("FieldByName(\"Num\") value:%v\n", value)// 获取嵌套结构体的字段// Cnt field:[1 2 3]fmt.Printf("Cnt field:%+v\n", valueS.FieldByIndex([]int{2, 0}))
}

3.2 遍历array、slice

通过 func (v Value) Index(i int) Value 可以通过下标来访问 Array,Slice,或者 String 各个元素的值。

package mainimport ("fmt""reflect"
)func main() {s := []int64{1, 2, 3, 4, 5, 6}valueS := reflect.ValueOf(s)// ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6fmt.Printf("ValueOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())for i := 0; i < valueS.Len(); i++ {// valueS.Index(0):1// valueS.Index(1):2// valueS.Index(2):3// valueS.Index(3):4// valueS.Index(4):5// valueS.Index(5):6fmt.Printf("valueS.Index(%v):%v\n", i, valueS.Index(i))}
}

3.3 遍历map

reflect 有两种方法遍历 map

  • 通过迭代器 MapIter 遍历 map

  • 先获取 map 的所有 key,再通过 key 获取对应的 value

package mainimport ("fmt""reflect"
)func main() {m := map[int]string{1: "1",2: "2",3: "3",}valueM := reflect.ValueOf(m)// 迭代器访问iter := valueM.MapRange()for iter.Next() {// key:1 val:1// key:2 val:2// key:3 val:3fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())}// ------fmt.Println("------")// 通过key访问keys := valueM.MapKeys()for i := 0; i < len(keys); i++ {// key:1 val:1// key:2 val:2// key:3 val:3fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))}
}

4、反射的三大定律

反射的两个基础函数定义:

  • 获取类型 func TypeOf(i any) Type
  • 获取值 func ValueOf(i any) Value

其中,any 是 interface{} 的别名。

interface{} 是不包含任何方法签名的空接口,任何类型都实现了空接口。

因此,interface{} 可以承载任何变量的 (value, concrete type)信息。

4.1 从interface到反射对象

interface{} 承载变量的 (value, concrete type) 信息,通过反射暴露方法来访问 interface{} 的值和类型。

可以简单理解为 interface{} 的值和信息传递到 reflect.Type 和 reflect.Value,方便获取。

4.2 从反射对象到interface

可以通过函数 func (v Value) Interface() (i any) 将反射对象转换为 interface{},是 func ValueOf(i any) Value的反

向操作。

package mainimport ("fmt""reflect"
)func main() {a := int32(10)valueA := reflect.ValueOf(a)// ValueOf(a):10fmt.Printf("ValueOf(a):%v\n", valueA)// Interface():10fmt.Printf("Interface():%v\n", valueA.Interface())ai, ok := valueA.Interface().(int32)// ok:true val:10fmt.Printf("ok:%v val:%v\n", ok, ai)
}

4.3 通过反射修改对象,该对象值必须是可修改的

reflect提供 func (v Value) CanSet() bool 判断对象值是否修改,通过 func (v Value) Set(x Value) 修改对象值。

package mainimport ("fmt""reflect"
)func main() {a := int32(10)valueA := reflect.ValueOf(a)// valueA :falsefmt.Printf("valueA :%v\n", valueA.CanSet())b := int32(100)valuePtrB := reflect.ValueOf(&b)// valuePtrB:false Elem:truefmt.Printf("valuePtrB:%v Elem:%v\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))// b:200 Elem:200fmt.Printf("b:%v Elem:%v\n", b, valuePtrB.Elem())
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5653.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

音视频开发-ffmpeg介绍-系列二

目录 一、FFmpeg核心结构体 二、解码流程 三、FFmpeg解码实现 四、FFmpeg编码实现 五、FFmpeg转码实现 一、FFmpeg核心结构体 AVFormatContext&#xff1a;解封装功能的结构体&#xff0c;包含文件名、音视频流、时长、比特率等信息&#xff1b; AVCodecContext&#xf…

【算法基础:数学知识】4.3 欧拉函数

文章目录 欧拉函数定义性质 例题列表873. 欧拉函数&#xff08;使用质因数分解求一个数的欧拉函数&#xff09;原理讲解&#xff08;公式推导&#xff09;⭐解法代码 874. 筛法求欧拉函数&#xff08;求 1 ~ n 中所有数字的欧拉函数&#xff09;⭐ 欧拉函数 https://oi-wiki.o…

[数据结构 -- 手撕排序算法第六篇] 递归实现快速排序(集霍尔版本,挖坑法,前后指针法为一篇的实现方法,很能打)

目录 1、常见的排序算法 1.1 交换排序基本思想 2、快速排序的实现方法 2.1 基本思想 3 hoare&#xff08;霍尔&#xff09;版本 3.1 实现思路 3.2 思路图解 3.3 为什么实现思路的步骤2、3不能交换 3.4 hoare版本代码实现 3.5 hoare版本代码测试 4、挖坑法 4.1 实现…

Java设计模式解析:迭代器模式的实现与应用

迭代器模式是一种行为型设计模式&#xff0c;它提供了一种统一的方式来遍历集合中的元素&#xff0c;而无需暴露集合的内部结构。在本文中&#xff0c;我们将深入探讨迭代器模式的实现和应用。 在软件开发中&#xff0c;集合是一种常见的数据结构&#xff0c;用于存储和管理一…

【手撕排序算法】---基数排序

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【数据结构初阶&#xff08;C实现&#xff09;】 我们直到一般的排序都是通过关键字的比较和移动这两种操作来进行排序的。 而今天介绍的…

​MySQL高阶语句(三)

目录 1、内连接 2、左连接 3、右连接&#xff1a; 二、存储过程⭐⭐⭐ 4. 调用存储过程 5.查看存储过程 5.1 查看存储过程 5.2查看指定存储过程信息 三. 存储过程的参数 3.1存储过程的参数 3.2修改存储过程 四.删除存储过程 MySQL 的连接查询&#xff0c;通常都是将来…

微服务一 实用篇 - 5.分布式搜索引擎(ElasticSearch基础)

《微服务一 实用篇 - 5.分布式搜索引擎&#xff08;ElasticSearch基础&#xff09;》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《微服务一 实用篇 - 5.分布式搜索引擎&#xff08;ElasticSearch基础&#xff09;》 《微服务一 实用篇 - 5.分布式搜索…

【广州华锐互动】列车人员疏散VR虚拟演练系统

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐应用于各个领域。在火车站安全方面&#xff0c;为了提高旅客的安全意识和应对突发事件的能力&#xff0c;列车人员疏散VR虚拟演练系统应运而生。 列车人员疏散VR虚拟演练系统是一种基于虚拟现实技术的教育培训系统&…

数据结构问答7

1. 图的定义和相关术语 答: 定义:图是由顶点集V和边集E组成,其中V为有限非空集。 相关术语:n个顶点,e条边,G=(V,E) ① 邻接点和端点:无向图中,若存在一条边(i, j),则称i,j为该边的端点,且它们互为邻接点;在有向图中,若存在一条边<i, j>,则称i,j分别为…

js浅拷贝与深拷贝

var value 10; var valueCopy value; valueCopy 1000 console.log(value, valueCopy) // 10 1000以上value结果的值没有随着valueCopy的改变而改变是因为数字&#xff0c;字符串是原始值&#xff0c;当重新给它赋值时不会在原地址修改而是新开一块地址 引用类型就是指向同…

【Vue】day03-VueCli(脚手架)

day03 一、今日目标 1.生命周期 生命周期介绍 生命周期的四个阶段 生命周期钩子 声明周期案例 2.综合案例-小黑记账清单 列表渲染 添加/删除 饼图渲染 3.工程化开发入门 工程化开发和脚手架 项目运行流程 组件化 组件注册 4.综合案例-小兔仙首页 拆分模块-局部…

SpringBoot 如何使用 MockMvc 进行 Web 集成测试

SpringBoot 如何使用 MockMvc 进行 Web 集成测试 介绍 SpringBoot 是一个流行的 Java Web 开发框架&#xff0c;它提供了一些强大的工具和库&#xff0c;使得开发 Web 应用程序变得更加容易。其中之一是 MockMvc&#xff0c;它提供了一种测试 SpringBoot Web 应用程序的方式&…

什么是神经网络?

我们常常使用深度学习来指训练神经网络的过程。 在这里举一个房屋价格预测的例子&#xff1a;假设有一个数据集&#xff0c;它包含了六栋房子的信息。所以&#xff0c;你知道房屋的面积是多少平方米&#xff0c;并且知道这个房屋的价格。这是&#xff0c;你想要拟合一个根据房屋…

el-form 初值和resetFields问题

配置片段。 给dataForm动态赋初值&#xff0c;必须如下&#xff0c;用 this.dataForm{}这种方式&#xff0c;其他的形式如 this.dataForm.属性、this.dataForm[属性] 都会失败且造成 el-form和resetFields 出现问题 <el-formref"dataForm":model"dataForm&qu…

【论文阅读】DQnet: Cross-Model Detail Querying for Camouflaged Object Detection

DQnet: Cross-Model Detail Querying for Camouflaged Object Detection DQnet&#xff1a;伪装目标检测中的跨模型细节查询 论文地址&#xff1a;https://arxiv.org/abs/2212.08296 这篇文章提出了一个交叉模型框架&#xff08;CNN-Transformer并行&#xff09;来检测伪装目…

【自启动配置】Ubuntu 设置开机自启动脚本

Ubuntu 开机运行的脚本和当前操作系统运行的级别有关&#xff0c;OS 的运行级别大概分为七个 目录 1、查看 OS 运行级别 2、创建自启动脚本 3、添加软链接 1、查看 OS 运行级别 输入命令 runlevel 查看当前系统运行级别。当前系统的运行级别为 5 2、创建自启动脚本 在 /et…

实训笔记7.19

实训笔记7.19 7.19一、座右铭二、Hadoop的HDFS分布式文件存储系统的相关原理性内容2.1 HDFS上传数据的流程2.2 HDFS下载数据的流程2.3 HDFS中NameNode和SecondaryNameNode工作机制&#xff08;涉及到HDFS的元数据管理操作&#xff09;2.4 HDFS中NameNode和DataNode的工作机制&a…

easyui tabs切换

easyui tabs切换之前提醒保存修改的信息 当存在多个tabs,相互切换时提醒保存修改的信息&#xff1a; 这里用的鼠标mousedown事件 var tabs $(#tabsId).tabs().tabs(tabs);for(var item0; item<tabs.length; item){tabs[item].panel(options).tab.unbind().bind(mousedow…

小程序自定义步骤条实现

效果展示&#xff1a; 支持背景颜色自定义 <view class"hl_steps"><view class"hl_steps_item" wx:for"{{steps}}" wx:key"id"><view class"hl_steps_item_circle_out" style"background-color: {{col…

【SpringCloud Alibaba】(二)微服务环境搭建

1. 项目流程搭建 整个项目主要分为 用户微服务、商品微服务和订单微服务&#xff0c;整个过程模拟的是用户下单扣减库存的操作。这里&#xff0c;为了简化整个流程&#xff0c;将商品的库存信息保存到了商品数据表&#xff0c;同时&#xff0c;使用商品微服务来扣减库存。小伙…