第十七:go 反射

   fmt.printf("%T",obj)   // 打印 reflect 的类型 

fmt.Printf("%T", obj) // *reflect.rtype  //打印的是一个指针类型

reflect包

 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Typereflect.Value两部分组成,并且reflect包提供了reflect.TypeOfreflect.ValueOf两个函数来获取任意对象的Value和Type。

TypeOf

在Go语言中,使用reflect.TypeOf()函数可以获得任意值的类型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。

package mainimport ("fmt""reflect"
)func reflectType(x interface{}) {v := reflect.TypeOf(x)fmt.Printf("type:%v\n", v)
}
func main() {var a float32 = 3.14reflectType(a) // type:float32var b int64 = 100reflectType(b) // type:int64
}

种类(Kind)就是指底层的类型

package mainimport ("fmt""reflect"
)type myInt int64func reflectType(x interface{}) {t := reflect.TypeOf(x)fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind())
}func main() {var a *float32 // 指针var b myInt    // 自定义类型var c rune     // 类型别名reflectType(a) // type: kind:ptrreflectType(b) // type:myInt kind:int64reflectType(c) // type:int32 kind:int32type person struct {name stringage  int}type book struct{ title string }var d = person{name: "沙河小王子",age:  18,}var e = book{title: "《跟小王子学Go语言》"}reflectType(d) // type:person kind:structreflectType(e) // type:book kind:struct
}

注意:Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回

ValueOf

reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。

reflect.Value类型提供的获取原始值的方法如下:

方法说明
Interface() interface {}将值以 interface{} 类型返回,可以通过类型断言转换为指定类型
Int() int64将值以 int 类型返回,所有有符号整型均可以此方式返回
Uint() uint64将值以 uint 类型返回,所有无符号整型均可以此方式返回
Float() float64将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回
Bool() bool将值以 bool 类型返回
Bytes() []bytes将值以字节数组 []bytes 类型返回
String() string将值以字符串类型返回

通过反射获取值

func reflectValue(x interface{}) {v := reflect.ValueOf(x)k := v.Kind()switch k {case reflect.Int64:// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换fmt.Printf("type is int64, value is %d\n", int64(v.Int()))case reflect.Float32:// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换fmt.Printf("type is float32, value is %f\n", float32(v.Float()))case reflect.Float64:// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换fmt.Printf("type is float64, value is %f\n", float64(v.Float()))}
}
func main() {var a float32 = 3.14var b int64 = 100reflectValue(a) // type is float32, value is 3.140000reflectValue(b) // type is int64, value is 100// 将int类型的原始值转换为reflect.Value类型c := reflect.ValueOf(10)fmt.Printf("type c :%T\n", c) // type c :reflect.Value
}

通过反射设置变量的值

想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。

package mainimport ("fmt""reflect"
)func reflectSetValue1(x interface{}) {v := reflect.ValueOf(x)if v.Kind() == reflect.Int64 {v.SetInt(200) //修改的是副本,reflect包会引发panic}
}
func reflectSetValue2(x interface{}) {v := reflect.ValueOf(x)// 反射中使用 Elem()方法获取指针对应的值if v.Elem().Kind() == reflect.Int64 {v.Elem().SetInt(200)}
}
func main() {var a int64 = 100// reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable valuereflectSetValue2(&a)fmt.Println(a)
}

isNil()和isValid()

isNil()
func (v Value) IsNil() bool

IsNil()报告v持有的值是否为nil。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic。

isValid()
func (v Value) IsValid() bool

IsValid()返回v是否持有一个值。如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。

举个例子

IsNil()常被用于判断指针是否为空;IsValid()常被用于判定返回值是否有效。

func main() {// *int类型空指针var a *intfmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())// nil值fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())// 实例化一个匿名结构体b := struct{}{}// 尝试从结构体中查找"abc"字段fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())// 尝试从结构体中查找"abc"方法fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())// mapc := map[string]int{}// 尝试从map中查找一个不存在的键fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
}

结构体反射

与结构体相关的方法

任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()Field()方法获得结构体成员的详细信息。

reflect.Type中与获取结构体成员相关的的方法如下表所示。

方法说明
Field(i int) StructField根据索引,返回索引对应的结构体字段的信息。
NumField() int返回结构体成员字段数量。
FieldByName(name string) (StructField, bool)根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool)根据传入的匹配函数匹配需要的字段。
NumMethod() int返回该类型的方法集中方法的数目
Method(int) Method返回该类型方法集中的第i个方法
MethodByName(string)(Method, bool)根据方法名返回该类型方法集中的方法

结构体反射示例

当我们使用反射得到一个结构体数据之后可以通过索引依次获取其字段信息,也可以通过字段名去获取指定的字段信息。

type student struct {Name  string `json:"name"`Score int    `json:"score"`
}func main() {stu1 := student{Name:  "小王子",Score: 90,}t := reflect.TypeOf(stu1)fmt.Println(t.Name(), t.Kind()) // student struct// 通过for循环遍历结构体的所有字段信息for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))}// 通过字段名获取指定结构体字段信息if scoreField, ok := t.FieldByName("Score"); ok {fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))}
}

反射是把双刃剑

反射是一个强大并富有表现力的工具,能让我们写出更灵活的代码。但是反射不应该被滥用,原因有以下三个。

  1. 基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。
  2. 大量使用反射的代码通常难以理解。
  3. 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。

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

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

相关文章

热门的壁纸创作风格呈现多元化发展趋势

下热门的壁纸创作风格呈现多元化发展趋势&#xff0c;以下是几种主流风格及其特点&#xff1a; 简约现代风格 流行元素&#xff1a;以简洁的线条、纯净的色彩块面和少量的抽象图形为主。摒弃过多繁杂的装饰&#xff0c;强调形式追随功能的设计理念。热度分析&#xff1a;在各大…

【SpringMVC】深入解析使用 Postman 在请求中传递对象类型、数组类型、参数类型的参数方法和后端参数重命名、及非必传参数设置的方法

SpringMVC—请求传参 1. 传递对象 如果参数比较多时&#xff0c;方法声明就需要有很多形参&#xff1b;并且后续每次新增一个参数&#xff0c;也需要修改方法声明. 我们不妨把这些参数封装为一个对象&#xff1b; Spring MVC 也可以自动实现对象参数的赋值&#xff0c;比如 Us…

AI智能眼镜的视觉革命:算法如何重塑人机交互新纪元

引言&#xff1a;视觉算法的核心地位与AI智能眼镜的崛起 AI智能眼镜作为下一代交互终端&#xff0c;其核心价值在于将视觉感知与人工智能深度融合&#xff0c;通过实时环境解析与动态反馈&#xff0c;重新定义人机交互的边界。据预测&#xff0c;2025年全球AI智能眼镜销量将突…

掌握 ArcGIS Pro:古地图制作技巧与方法

在探索历史的长河中&#xff0c;古地图以其独特的魅力承载着丰富的地理信息和历史文化价值。 随着技术的进步&#xff0c;现代地理信息系统&#xff08;GIS&#xff09;如ArcGIS Pro为我们提供了强大的工具&#xff0c;使制作古地图成为可能。 本文将详细介绍如何使用ArcGIS …

MySQL的安装及配置

一.以安装包方式下载 1.进入MySQL官网&#xff0c;下载安装包 官网链接&#xff1a;https://downloads.mysql.com/archives/installer/ 2.安装MySQL 二.压缩包方式下载 下载位置&#xff1a;mysql下载位置 解压缩后位置&#xff1a;D:\mysql-8.0.15-winx64 在主目录下复制…

Python实例:PyMuPDF实现PDF翻译,英文翻译为中文,并按段落创建中文PDF

基于PyMuPDF与百度翻译的PDF翻译处理系统开发:中文乱码解决方案与自动化排版实践 一 、功能预览:将英文翻译为中文后创建的PDF 二、完整代码 from reportlab.lib.pagesizes import letter from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle

『VUE』vue 引入Font Awesome图标库(详细图文注释)

目录 Font Awesome介绍安装引入npm 安装导入src/main.js 使用总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 Font Awesome介绍 我一般是中文网搜索找到图标的英文名然后去官方网站搜索 官方网站(英文名搜索) https://font…

0基础 | 看懂原理图Datasheet 系列1

原理图功能分类 控制部分&#xff1a;整个电路板的核心控制和计算部分&#xff08;CPU&#xff09; CPU最小系统是什么?电源时钟复位 接口部分&#xff1a;实现特定功能的部分 如Wife模块、通讯接口 电源部分&#xff1a;整个电路板的供电部分 任何电路板都是必要的&…

python-leetcode-删掉一个元素以后全为 1 的最长子数组

1493. 删掉一个元素以后全为 1 的最长子数组 - 力扣(LeetCode) 可以使用滑动窗口的方式来解决这个问题。我们要找到最长的全 1 子数组,但必须删除一个元素,因此可以将问题转化为寻找最多包含一个 0 的最长子数组。 解题思路 使用双指针(滑动窗口),维护窗口内最多包含一…

ubuntu挂载新硬盘

在准备数据的时候出现空间不够的问题&#xff1a; 添加200G的空间&#xff0c;挂在到/home/vipuser/BEV_Depth/BEVDepth/data/nuScenes 使用lsblk查看挂载的盘 有两块硬盘 vda 和 vdb&#xff0c;其中 vda 已经用于系统安装&#xff0c;vdb 尚未分区和挂载。 1. **分区新磁盘…

Java 生成图形验证码

一、图形验证码的意义 图形验证码是一种广泛应用于互联网领域的安全验证机制&#xff0c;它通过向用户展示包含字符、数字、图形等信息的图片&#xff0c;要求用户正确识别并输入其中的内容&#xff0c;以此来区分用户是人类还是机器程序。图形验证码具有多方面重要意义&#…

Qwen/QwQ-32B 基础模型上构建agent实现ppt自动生成

关心Qwen/QwQ-32B 性能测试结果可以参考下 https://zhuanlan.zhihu.com/p/28600079208https://zhuanlan.zhihu.com/p/28600079208 官方宣传上是该模型性能比肩满血版 DeepSeek-R1&#xff08;671B&#xff09;&#xff01; 我们实现一个 使用Qwen/QwQ-32B 自动生成 PowerPoi…

ios 小组件和数据共享

创建主工程就不必讲了 1 创建小组件 创建子工程 [new Target ] 选择 [ Widger Extension] 小组件入口是WidgetBundle文件&#xff0c;可以进行多个小组件的调试 TestWidget2文件是主要操作&#xff0c;小组件使用swiftUI布局&#xff0c;使用 AppIntent进行事件处理&#xff…

C++【类和对象】(超详细!!!)

C【类和对象】 1.运算符重载2.赋值运算符重载3.日期类的实现 1.运算符重载 (1).C规定类类型运算符使用时&#xff0c;必须转换成调用运算符重载。 (2).运算符重载是具有特殊名字的函数&#xff0c;名字等于operator加需要使用的运算符&#xff0c;具有返回类型和参数列表及函数…

ExBody2: Advanced Expressive Humanoid Whole-Body Control

ExBody2: Advanced Expressive Humanoid Whole-Body Control 研究动机解决方案技术路线实验结果 ExBody2: Advanced Expressive Humanoid Whole-Body Control 研究动机 高维状态空间和复杂控制限制了人形机器人在现实世界中的应用。人形机器人和人类在身体结构上有很大不同&a…

115.不同的子序列(困难)

115.不同的子序列 力扣题目链接(opens new window) 给定一个字符串 s 和一个字符串 t &#xff0c;计算在 s 的子序列中 t 出现的个数。 字符串的一个 子序列 是指&#xff0c;通过删除一些&#xff08;也可以不删除&#xff09;字符且不干扰剩余字符相对位置所组成的新字符…

【学习笔记】【AI医生】2-4 项目详细分析及DeepSeek适用场景

【DeepSeek AI 医生】2-4 项目详细分析及DeepSeek适用场景 1.1 项目流程图1.2 主流AI模型对比1.3 DeepSeek使用途径1.4 DeepSeek 适用场景 1.1 项目流程图 1.2 主流AI模型对比 1.3 DeepSeek使用途径 官网 https://chat.deepseek.com/线上Api &#xff08;目前不可以状态&#…

再聊 Flutter Riverpod ,注解模式下的 Riverpod 有什么特别之处,还有发展方向

三年前我们通过 《Flutter Riverpod 全面深入解析》 深入理解了 riverpod 的内部实现&#xff0c;而时隔三年之后&#xff0c;如今Riverpod 的主流模式已经是注解&#xff0c;那今天就让我们来聊聊 riverpod 的注解有什么特殊之处。 前言 在此之前&#xff0c;我们需要先回忆…

Java【多线程】(3)单例模式与线程安全

目录 1.前言 2.正文 2.1线程安全类 2.2杂谈&#xff08;介绍几个概念&#xff09; 2.2.1内存可见性 2.2.2指令重排序 2.2.3线程饥饿 1. 什么是线程饥饿&#xff1f; 2. 线程饥饿的常见原因 2.2.4区分wait和sleep 2.4单例模式 2.4.1饿汉模式 2.4.2懒汉模式 2.4.2指…

4g串口发短信踩坑

这短短的4行有三种发送方式 1 勾选新行 2 不选新行 3 不选新行&#xff0c;再勾选16进制&#xff0c;完美解决 推荐网站AIR780 MINI LTE 4G全网通模块 — wiki