当前位置: 首页 > news >正文

Go语言反射从入门到进阶

一、反射的基础概念

在 Go 语言中,反射是程序在运行时检查和修改自身状态的能力。通过反射,我们可以在运行时获取变量的类型信息、查看结构体的字段、调用方法等。Go 语言的反射功能主要通过 reflect 包实现。

1.1 反射的基本类型:Type 和 Value

Go 的反射主要基于两个重要的类型:

  • reflect.Type:表示 Go 类型的接口
  • reflect.Value:表示 Go 值的接口

让我们从一个简单的例子开始:

package mainimport ("fmt""reflect"
)func main() {var x float64 = 3.14// 获取变量的类型信息t := reflect.TypeOf(x)fmt.Printf("类型:%v\n", t)// 获取变量的值信息v := reflect.ValueOf(x)fmt.Printf("值:%v\n", v)
}

二、基本类型的反射操作

2.1 获取类型信息

package mainimport ("fmt""reflect"
)func main() {var num int64 = 42var str string = "hello"// 获取基本类型信息fmt.Printf("num 的类型:%v\n", reflect.TypeOf(num))fmt.Printf("str 的类型:%v\n", reflect.TypeOf(str))// 获取类型的种类(Kind)fmt.Printf("num 的种类:%v\n", reflect.TypeOf(num).Kind())fmt.Printf("str 的种类:%v\n", reflect.TypeOf(str).Kind())
}

2.2 获取和修改值

package mainimport ("fmt""reflect"
)func main() {x := 3.14v := reflect.ValueOf(&x) // 注意:这里传入指针// 检查值是否可以被修改if v.Kind() == reflect.Ptr && v.Elem().CanSet() {v.Elem().SetFloat(2.718)}fmt.Printf("修改后的值:%v\n", x)
}

三、结构体的反射操作

3.1 基本结构体反射

package mainimport ("fmt""reflect"
)type Person struct {Name string `json:"name"`Age  int    `json:"age"`
}func main() {p := Person{Name: "张三",Age:  25,}t := reflect.TypeOf(p)// 遍历结构体字段for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Printf("字段名:%s\n", field.Name)fmt.Printf("字段类型:%v\n", field.Type)fmt.Printf("标签:%v\n", field.Tag.Get("json"))}
}

3.2 动态调用方法

package mainimport ("fmt""reflect"
)type Person struct {Name stringAge  int
}func (p Person) SayHello(msg string) string {return fmt.Sprintf("Hello, %s. I am %s", msg, p.Name)
}func main() {p := Person{Name: "张三", Age: 25}// 获取方法v := reflect.ValueOf(p)method := v.MethodByName("SayHello")// 准备参数args := []reflect.Value{reflect.ValueOf("世界")}// 调用方法result := method.Call(args)fmt.Println(result[0].String())
}

四、高级应用场景

4.1 通用的结构体字段验证器

package mainimport ("fmt""reflect""strings"
)type User struct {Name  string `validate:"required,min=3"`Email string `validate:"required,email"`Age   int    `validate:"required,min=18"`
}func validate(v interface{}) []string {var errors []stringt := reflect.TypeOf(v)val := reflect.ValueOf(v)for i := 0; i < t.NumField(); i++ {field := t.Field(i)value := val.Field(i)// 获取验证规则rules := strings.Split(field.Tag.Get("validate"), ",")for _, rule := range rules {switch {case rule == "required":if value.Interface() == reflect.Zero(value.Type()).Interface() {errors = append(errors, fmt.Sprintf("%s 是必填字段", field.Name))}case strings.HasPrefix(rule, "min="):// 这里简化处理,实际应用中需要更复杂的验证逻辑if value.Kind() == reflect.String && len(value.String()) < 3 {errors = append(errors, fmt.Sprintf("%s 长度不能小于3", field.Name))}}}}return errors
}func main() {user := User{Name:  "张",Email: "invalid-email",Age:   16,}if errors := validate(user); len(errors) > 0 {fmt.Printf("验证错误:\n%s\n", strings.Join(errors, "\n"))}
}

4.2 通用的 JSON 序列化器

package mainimport ("fmt""reflect""strings"
)func toJSON(v interface{}) string {t := reflect.TypeOf(v)val := reflect.ValueOf(v)if t.Kind() != reflect.Struct {return fmt.Sprintf("\"%v\"", val.Interface())}var pairs []stringfor i := 0; i < t.NumField(); i++ {field := t.Field(i)value := val.Field(i)// 获取json标签jsonTag := field.Tag.Get("json")if jsonTag == "" {jsonTag = field.Name}pair := fmt.Sprintf("\"%s\":%v", jsonTag, toJSON(value.Interface()))pairs = append(pairs, pair)}return "{" + strings.Join(pairs, ",") + "}"
}type Address struct {Street string `json:"street"`City   string `json:"city"`
}type Person struct {Name    string  `json:"name"`Age     int     `json:"age"`Address Address `json:"address"`
}func main() {p := Person{Name: "张三",Age:  25,Address: Address{Street: "中关村大街",City:   "北京",},}fmt.Println(toJSON(p))
}

五、反射的最佳实践

  1. 谨慎使用反射

    • 反射会带来性能开销
    • 代码可读性可能降低
    • 编译时类型检查被绕过
  2. 适合使用反射的场景

    • 需要处理未知类型的数据
    • 需要动态调用方法
    • 需要实现通用的框架或库
    • 需要根据配置动态创建对象
  3. 性能优化建议

    • 缓存反射结果
    • 避免重复获取 Type 和 Value
    • 在性能敏感的代码路径上避免使用反射

六、总结

Go 语言的反射机制为我们提供了强大的运行时类型信息和值操作能力。通过反射,我们可以:

  • 检查类型信息
  • 获取和修改值
  • 访问结构体字段和方法
  • 实现通用的框架和工具

但是,反射也带来了性能开销和代码复杂性,因此应该在合适的场景下谨慎使用。在实际开发中,建议遵循"实用性"原则,在确实需要反射的场景下再使用它。

相关文章:

Go语言反射从入门到进阶

一、反射的基础概念 在 Go 语言中&#xff0c;反射是程序在运行时检查和修改自身状态的能力。通过反射&#xff0c;我们可以在运行时获取变量的类型信息、查看结构体的字段、调用方法等。Go 语言的反射功能主要通过 reflect 包实现。 1.1 反射的基本类型&#xff1a;Type 和 …...

【基于rust-wasm的前端页面转pdf组件和示例】

基于rust-wasm前端页面转pdf组件和示例 朔源多余的废话花哨的吹牛那点东西要不要拿来试试事到如今 做个美梦 我觉得本文的意义在于,wasm扩展了浏览器的边界,但是又担心如同java的web applet水土不服. 如同我至今看不出塞班和iOS的不同下载地址&#xff1a;在github的备份 朔源…...

ARM64 Windows 10 IoT工控主板运行x86程序效率测试

ARM上的 Windows 10 IoT 企业版支持仿真 x86 应用程序&#xff0c;而 ARM上的 Windows 11 IoT 企业版则支持仿真 x86 和 x64 应用程序。英创推出的名片尺寸ARM64工控主板ESM8400&#xff0c;可预装正版Windows 10 IoT企业版操作系统&#xff0c;x86程序可无需修改而直接在ESM84…...

开放世界目标检测 Grounding DINO

开放世界目标检测 Grounding DINO flyfish Grounding DINO 是一种开创性的开放集对象检测器&#xff0c;它通过结合基于Transformer的检测器DINO与基于文本描述的预训练技术&#xff0c;实现了可以根据人类输入&#xff08;如类别名称或指代表达&#xff09;检测任意对象的功…...

easegen将教材批量生成可控ppt课件方案设计

之前客户提出过一个需求&#xff0c;就是希望可以将一本教材&#xff0c;快速的转换为教学ppt&#xff0c;虽然通过人工程序脚本的方式&#xff0c;已经实现了该功能&#xff0c;但是因为没有做到通用&#xff0c;每次都需要修改脚本&#xff0c;无法让客户自行完成所有流程&am…...

2002 - Can‘t connect to server on ‘192.168.1.XX‘ (36)

参考:2002 - Can‘t connect to server on ‘192.168.1.XX‘ (36) ubantu20.04&#xff0c;mysql5.7.13 navicat 远程连接数据库报错 2002 - Can’t connect to server on ‘192.168.1.61’ (36) 一、查看数据库服务是否有启动&#xff0c;发现有启动 systemctl status mysql…...

【虚拟机网络拓扑记录】

虚拟机网络拓扑记录 虚拟机安装windows到ubuntu的网络拓扑ubuntu到ubuntu里面的虚拟机网络拓扑windows到ubuntu里面的虚拟机网络拓扑 虚拟机安装 本实验宿主机为windos&#xff0c; 安装vmware&#xff0c;虚拟机操作系统使用ubuntu&#xff0c;然后再在ubuntu上面创建新的虚拟…...

【单片机通讯协议】—— 常用的UART/I2C/SPI等通讯协议的基本原理与时序分析

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、通信基本知识1.1 MCU的参见外设1.2 通信的分类按基本的类型从传输方向上来分 二、UART&#xff08;串口通讯&#xff09;2.1 简介2.2 时序图分析2.3 UART的…...

Vue3 核心语法

1. OptionsAPI 与 CompositionAPI Vue2 的API设计是 Options&#xff08;配置&#xff09;风格的。Vue3 的API设计是 Composition&#xff08;组合&#xff09;风格的。 1.1 Options API 的弊端 Options类型的 API&#xff0c;数据、方法、计算属性等&#xff0c;是分散在&a…...

LLaMA-Factory GLM4-9B-CHAT LoRA 指令微调实战

&#x1f929;LLaMA-Factory GLM LoRA 微调 安装llama-factory包 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git进入下载好的llama-factory&#xff0c;安装依赖包 cd LLaMA-Factory pip install -e ".[torch,metrics]" #上面这步操作会完成…...

GTM023 W.H.Greub线性代数经典教材:Linear Algebra

这本教材是我高中时期入门线性代数的主要教材&#xff0c;我的很多基础知识都来源于这本书&#xff0c;如今看回这本书可以说满满的回忆。这本书可以说&#xff0c;是我读过的内容最为全面且完备的线性代数教材了。而且它的语言风格非常的代数化&#xff0c;没有什么直观可言&a…...

交换机与路由器的区别

交换机和路由器是网络中的两种关键设备&#xff0c;它们各自承担不同的功能&#xff0c;主要区别体现在以下几个方面&#xff1a; 一、工作层次与功能 交换机&#xff1a; 工作层次&#xff1a;交换机主要工作在OSI模型的第二层&#xff0c;即数据链路层。 功能&#xff1a;交…...

springboot502基于WEB的牙科诊所管理系统(论文+源码)_kaic

牙科诊所管理系统的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定牙科诊所管理系统的…...

soular使用教程

用 soular 配置你的组织&#xff0c;工作更高效&#xff01;以下是快速上手的简单步骤&#xff1a; &#xfeff; 1. 账号管理 可以对账号信息进行多方面管理&#xff0c;包括分配不同的部门、用户组等&#xff0c;从而确保账号权限和职责的清晰分配。 &#xfeff; 1.1 用…...

纯div+css+js弹出窗

目的&#xff1a;实现弹出窗、仅关闭弹窗之后才能操作。自适应宽度与高度、当文本内容太多时、添加滚动条效果。 效果图 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport"…...

一篇文章学会HTML

目录 页面结构 网页基本标签 图像标签 超链接标签 文本链接 图像链接 锚链接 功能链接 列表 有序列表 无序列表 自定义列表 表格 跨列/跨行 表头 媒体元素 视频 音频 网站的嵌套 表单 表单元素 文本框 单选框 多选框 按钮 下拉框 文本域和文件域 表…...

QGIS二次开发(插件开发)

实习二 QGIS插件开发 2.1 任务要求 a&#xff09;用C语言编写qgis插件&#xff0c;实现带有x/y坐标的文本文件的地图显示。 用文件流fstream操作文本文件&#xff0c;读取其中的坐标数据。基于QgsPlugin相关类派生出一个插件&#xff0c;并加到插件工厂中。基于QgsVectorLaye…...

Web防火墙和下一代防火墙的区别

介绍 客户经常询问“当我已经拥有下一代防火墙&#xff08;NGFW&#xff09;时&#xff0c;为什么需要Web应用程序防火墙&#xff08;WAF&#xff09;&#xff1f;”。本博文的目的是解释两种解决方案之间的区别&#xff0c;重点关注Web应用程序防火墙可以提供的附加值。 什么…...

Linux:alias别名永久有效

一、背景 日常使用bash时候&#xff0c;有些常用的命令参数的组合命令太长&#xff0c;很难记&#xff0c;此时可以利用Linux提供的alias命令生成命令的别名&#xff08;命令的隐射&#xff09;&#xff0c;但是我们会发现&#xff0c;当退出了终端后重新登录就失效了&#xff…...

【递归与回溯深度解析:经典题解精讲(中篇)】—— LeetCode

文章目录 组合目标和组合总和字母大小写全排序优美的排列N皇后 组合 思路&#xff1a;回溯算法 问题要求从 1 到 n 中选出 k 个数的所有组合。 使用回溯算法递归构造解。 每次递归时&#xff0c;记录当前的组合路径&#xff0c;当组合长度达到 k 时&#xff0c;将其加入结果集…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...