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

【Golang】第十一弹------反射

 🎁个人主页星云爱编程

 🔍所属专栏:【Go】 

🎉欢迎大家点赞👍评论📝收藏⭐文章

 长风破浪会有时,直挂云帆济沧海

目录

1.反射基本介绍

2.反射重要的函数和概念

3.反射应用场景

4.反射最佳实现

结语


1.反射基本介绍

  • 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
  • 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  • 通过反射,可以修改变量的值,可以调用关联的方法。
  • 使用反射,需要 import("reflect")

2.反射重要的函数和概念

(1)reflect.TypeOf(i interface{}) Type

作用:获取接口值的类型信息。

t := reflect.TypeOf(42)
fmt.Println(t) // 输出: int

(2)reflect.ValueOf(i interface{}) Value

作用:获取接口值的反射对象,用于进一步操作值。

v := reflect.ValueOf("hello")
fmt.Println(v) // 输出: hello

(3)Value.Kind() Kind

作用:返回值的底层类型(如 int 、 float64 、 struct 等)

v := reflect.ValueOf(3.14)
fmt.Println(v.Kind()) // 输出: float64

(4)Value.Interface() interface{}

作用 :将反射对象转换回 interface{} 类型。

v := reflect.ValueOf(100)
i := v.Interface()
fmt.Println(i) // 输出: 100

(5)Value.Int() int64

作用 :获取 int 类型的值(适用于 int 、 int8 、 int16 、 int32 、 int64 )。

v := reflect.ValueOf(42)
fmt.Println(v.Int()) // 输出: 42

(6)Value.SetInt(i int64)
作用设置 int 类型的值(适用于 int 、 int8 、 int16 、 int32 、 int64 )。

var x int = 10
v := reflect.ValueOf(&x).Elem()
v.SetInt(20)
fmt.Println(x) // 输出: 20

(7)Value.Elem() Value

作用:获取指针或接口指向的实际值。

var x int = 10
v := reflect.ValueOf(&x).Elem()
fmt.Println(v.Int()) // 输出: 10

(8)Type.NumField() int

作用 :返回结构体的字段数量。

type Person struct {Name string Age int
}
t := reflect.TypeOf(Person{})
fmt.Println(t.NumField()) // 输出: 2

(9)Type.Field(i int) StructField
作用 :获取结构体的第 i 个字段的信息。

type Person struct { Name string; Age int }
t := reflect.TypeOf(Person{})
fmt.Println(t.Field(0).Name) // 输出: Name

(10)Value.Call(in []Value) []Value

作用:调用函数并返回结果。

func Add(a, b int) int { return a + b }
v := reflect.ValueOf(Add)
args := []reflect.Value{reflect.ValueOf(2), reflect.ValueOf(3)}
result := v.Call(args)
fmt.Println(result[0].Int()) // 输出: 5

3.反射应用场景

(1)动态类型检查

反射可以用于在运行时检查变量的类型,这在处理未知类型的数据时非常有用。例如,编写通用函数或库时,可能需要根据传入参数的类型执行不同的操作。

func checkType(data interface{}) {t := reflect.TypeOf(data)fmt.Println("Type:", t)
}

(2)动态调用方法

反射可以用于在运行时动态调用对象的方法,这在需要根据条件调用不同方法时非常有用

type MyStruct struct{}func (m *MyStruct) MyMethod() {fmt.Println("MyMethod called")
}func callMethod(obj interface{}, methodName string) {v := reflect.ValueOf(obj)method := v.MethodByName(methodName)if method.IsValid() {method.Call(nil)}
}

(3)结构体字段操作

反射可以用于在运行时动态访问和修改结构体的字段,这在处理配置文件、数据库映射等场景时非常有用。

type Person struct {Name stringAge  int
}func setField(obj interface{}, fieldName string, value interface{}) {v := reflect.ValueOf(obj).Elem()field := v.FieldByName(fieldName)if field.IsValid() && field.CanSet() {field.Set(reflect.ValueOf(value))}
}

(4)序列化与反序列化

反射可以用于实现通用的序列化和反序列化功能,例如将结构体转换为JSON或从JSON解析为结构体

func toJSON(obj interface{}) string {v := reflect.ValueOf(obj)t := reflect.TypeOf(obj)var result stringfor i := 0; i < v.NumField(); i++ {field := t.Field(i)value := v.Field(i)result += fmt.Sprintf("%s: %v\n", field.Name, value.Interface())}return result
}

(5)插件系统

反射可以用于实现插件系统,动态加载和调用插件中的函数或方法。

func loadPlugin(pluginPath string, functionName string) {p, err := plugin.Open(pluginPath)if err != nil {log.Fatal(err)}f, err := p.Lookup(functionName)if err != nil {log.Fatal(err)}f.(func())()
}

(6)依赖注入

反射可以用于实现依赖注入框架,动态地将依赖注入到对象中。

type Service struct {Dependency *Dependency
}func injectDependency(service interface{}, dependency interface{}) {v := reflect.ValueOf(service).Elem()field := v.FieldByName("Dependency")if field.IsValid() && field.CanSet() {field.Set(reflect.ValueOf(dependency))}
}

4.反射最佳实现

在 Go 语言中,反射虽然强大,但应谨慎使用,因为它会带来性能开销和代码可读性降低的问题。以下是反射的最佳实践和实现建议:

(1)避免过度使用反射

反射应作为最后的手段,优先使用静态类型检查和接口。只有在处理未知类型或动态数据时才使用反射。

(2)类型断言优先于反射

如果可以通过类型断言解决问题,优先使用类型断言,而不是反射。

func process(data interface{}) {if val, ok := data.(int); ok {fmt.Println("Integer:", val)} else if val, ok := data.(string); ok {fmt.Println("String:", val)}
}

(3)缓存反射结果

如果需要多次使用反射结果(如 Type 或 Value ),可以将其缓存起来,避免重复计算。

var cachedType reflect.Typefunc init() {cachedType = reflect.TypeOf(MyStruct{})
}func process(data interface{}) {if reflect.TypeOf(data) == cachedType {// 处理逻辑}
}

(4)使用 Kind() 检查类型

在处理反射时,优先使用 Kind() 检查底层类型,而不是直接比较 Type 。

func process(data interface{}) {v := reflect.ValueOf(data)if v.Kind() == reflect.Int {fmt.Println("Integer:", v.Int())}
}

(5)处理指针和值

反射时需要注意指针和值的区别,使用 Elem() 获取指针指向的值。

func modifyValue(data interface{}) {v := reflect.ValueOf(data).Elem()if v.CanSet() {v.SetInt(42)}
}

(6)处理结构体字段

在操作结构体字段时,确保字段可设置( CanSet() ),并使用 FieldByName 或 Field 访问字段。

type MyStruct struct {Name stringAge  int
}func setField(data interface{}, fieldName string, value interface{}) {v := reflect.ValueOf(data).Elem()field := v.FieldByName(fieldName)if field.IsValid() && field.CanSet() {field.Set(reflect.ValueOf(value))}
}

(7)处理方法和函数

反射可以动态调用方法和函数,但需要确保方法存在且参数匹配。

type MyStruct struct{}func (m *MyStruct) MyMethod() {fmt.Println("MyMethod called")
}func callMethod(data interface{}, methodName string) {v := reflect.ValueOf(data)method := v.MethodByName(methodName)if method.IsValid() {method.Call(nil)}
}

(8)错误处理

反射操作可能会引发 panic(如类型不匹配),因此需要做好错误处理。

func safeProcess(data interface{}) {defer func() {if err := recover(); err != nil {fmt.Println("Error:", err)}}()v := reflect.ValueOf(data)fmt.Println("Value:", v.Int())
}

综合案例:

package mainimport ("fmt""reflect"
)type MyStruct struct {Name stringAge  int
}func (m *MyStruct) MyMethod() {fmt.Println("MyMethod called")
}func process(data interface{}) {v := reflect.ValueOf(data).Elem()t := v.Type()// 遍历结构体字段for i := 0; i < v.NumField(); i++ {field := v.Field(i)fieldName := t.Field(i).Namefmt.Printf("Field %s: %v\n", fieldName, field.Interface())}// 调用方法method := v.MethodByName("MyMethod")if method.IsValid() {method.Call(nil)}
}func main() {obj := &MyStruct{Name: "Alice", Age: 30}process(obj)
}

结语

感谢您的耐心阅读,希望这篇博客能够为您带来新的视角和启发。如果您觉得内容有价值,不妨动动手指,给个赞👍,让更多的朋友看到。同时,点击关注🔔,不错过我们的每一次精彩分享。若想随时回顾这些知识点,别忘了收藏⭐,让知识触手可及。您的支持是我们前进的动力,期待与您在下一次分享中相遇!

相关文章:

【Golang】第十一弹------反射

&#x1f381;个人主页&#xff1a;星云爱编程 &#x1f50d;所属专栏&#xff1a;【Go】 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 长风破浪会有时&#xff0c;直挂云帆济沧海 目录 1.反射基本介绍 2.反射重要的函数和概念 3.反射应用场景 4.反…...

C#里使用libxl的对齐/边框/颜色

一份好的EXCEL文件,通道会有不同的颜色和边框来表示。 以便表示一些重要的信息,这样才能让人们一眼就看到需要关注的信息。 如下面所示: 要显示上面的内容,需要使用下面的例子: private void button12_Click(object sender, EventArgs e){var book = new ExcelBook();if…...

算法刷题记录——LeetCode篇(1.4) [第31~40题](持续更新)

更新时间&#xff1a;2025-03-29 算法题解目录汇总&#xff1a;算法刷题记录——题解目录汇总技术博客总目录&#xff1a;计算机技术系列博客——目录页 优先整理热门100及面试150&#xff0c;不定期持续更新&#xff0c;欢迎关注&#xff01; 32. 最长有效括号 给你一个只包…...

软考中级-软件设计师信息安全模块考点解析

一、防火墙技术 内部网络是 安全的可信赖的外部网络是不安全的不可信赖的外部网络和内部网络之间有一个DMZ隔离区&#xff0c; 可以在DMZ隔离区中搭建服务&#xff1a;例如&#xff1a;WEB服务器 安全排序&#xff1a;内网>DMZ>外网 三个发展阶段&#xff1a; 包过滤防…...

【蓝桥杯】每日练习 Day 16,17

前言 接下来是这两天的题目&#xff08;昨天主播打完模拟赛感觉身体被掏空所以没有写题目的总结&#xff09;&#xff0c;只有三道题。 一道并查集&#xff0c;一道单调栈和一道单调队列。 奶酪 分析 这是一道模板题&#xff08;连通块&#xff09;&#xff0c;只讲思路。 …...

相机租赁网站基于Spring Boot SSM

目录 摘要‌ 1. 项目背景与意义 2. 功能需求分析 3. 技术需求分析 ‌3.1开发语言‌&#xff1a;Java‌13。 3‌.2其他技术‌&#xff1a; 4. 系统设计与实现 5. 市场分析 6. 创新点与优势 7. 预期成果与展望 摘要‌ 随着摄影技术的普及和摄影爱好者数量的增加&#…...

树莓派超全系列文档--(14)无需交互使用raspi-config工具其一

无需交互使用raspi-config工具其一 无需交互的 raspi-configSystem optionsWireless LANAudioPasswordHostnameBoot/Auto loginNetwork at bootSplash screenPower LEDBrowser Display optionsUnderscanScreen blankingVNC resolutionComposite 文章来源&#xff1a; http://r…...

Linux驱动开发--IIC子系统

1.1 简介 I2C 是很常见的一种总线协议&#xff0c; I2C 是 NXP 公司设计的&#xff0c; I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线)&#xff0c;另外一条是 SDA(串行数据线)&#xff0c;这两条数据线需要接上拉电阻&#xff0c;总线空闲的时候 …...

如何应对硬件测试覆盖率不足导致量产故障

硬件测试覆盖率不足导致的量产故障是硬件制造领域的一大痛点。要有效应对&#xff0c;必须从提高测试覆盖率、优化测试方案、引入风险管理机制三个方面入手。其中&#xff0c;优化测试方案尤为关键&#xff0c;应从产品设计阶段开始&#xff0c;通过精确的测试用例规划、详细的…...

JS数组复制方法及注意事项

在 JavaScript 中&#xff0c;直接赋值数组会导致引用传递&#xff08;修改一个会影响另一个&#xff09;&#xff0c;因此需要创建数组的副本。以下是几种常见的浅拷贝方法&#xff1a; 1. 使用 slice() 方法 javascript const originalArray [1, 2, 3]; const copiedArra…...

【附JS、Python、C++题解】Leetcode面试150题(12)多数问题

一、题目 169. 多数元素 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。 多数元素是指在数组中出现次数大于[n/2]的元素。你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&a…...

Centos7 安装 TDengine

Centos7 安装 TDengine 1、简介 官网&#xff1a; https://www.taosdata.com TDengine 是一款开源、高性能、云原生的时序数据库&#xff08;Time Series Database, TSDB&#xff09;, 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。同时它还带有内建的缓…...

[特殊字符]《Curve DAO 系统学习目录》

本教程旨在系统学习 Curve DAO 项目的整体架构、核心机制、合约设计、治理逻辑与代币经济等内容&#xff0c;帮助开发者全面理解其设计理念及运作方式。 目录总览&#xff1a; 1. Curve 项目概览 • 1.1 Curve 是什么&#xff1f;主要解决什么问题&#xff1f; • 1.2 与其他…...

Pandas **Series**

以下是关于 Pandas Series 的从入门到精通的系统指南&#xff0c;包含核心概念、操作技巧和实战示例&#xff1a; 1. 入门篇&#xff1a;基础操作 1.1 创建Series import pandas as pd# 从列表创建 s1 pd.Series([1, 3, 5, 7, 9]) # 默认数字索引 s2 pd.Series([10, 20, 3…...

Kafka 多线程开发消费者实例

目前&#xff0c;计算机的硬件条件已经大大改善&#xff0c;即使是在普通的笔记本电脑上&#xff0c;多核都已经是标配了&#xff0c;更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序依然是单线程架构&#xff0c;那实在是有点暴殄天物了。不过&#xff0c;Kafka …...

Linux线程池实现

1.线程池实现 全部代码&#xff1a;whb-helloworld/113 1.唤醒线程 一个是唤醒全部线程&#xff0c;一个是唤醒一个线程。 void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepernum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程&q…...

Linux《进程概念(上)》

在之前的Linux学习当中我们已经了解了基本的Linux指令以及基础的开发工具的使用&#xff0c;那么接下来我们就要开始Linux当中一个非常重要的部分的学习——进程&#xff0c;在此进程是我们之后Linux学习的基础&#xff0c;并且通过进程的学习会让我们了解更多的操作系统的相关…...

【算法】并查集基础讲解

一、定义 一种树型的数据结构&#xff0c;用于处理一些不相交集合的合并及查询问题。思想是用一个数组表示了整片森林&#xff08;parent&#xff09;&#xff0c;树的根节点唯一标识了一个集合&#xff0c;只要找到了某个元素的的树根&#xff0c;就能确定它在哪个集合里。 …...

C++ STL常用算法之常用集合算法

常用集合算法 学习目标: 掌握常用的集合算法 算法简介: set_intersection // 求两个容器的交集 set_union // 求两个容器的并集 set_difference // 求两个容器的差集 set_intersection 功能描述: 求两个容器的交集 函数原型: set_intersection(iterator beg1, iterat…...

Qt warning LNK4042: 对象被多次指定;已忽略多余的指定

一、常规原因&#xff1a; pro或pri 文件中源文件被多次包含 解决&#xff1a;删除变量 SOURCES 和 HEADERS 中重复条目 二、误用 对于某些pri库可以使用如下代码简写包含 INCLUDEPATH $$PWDHEADERS $$PWD/*.hSOURCES $$PWD/*.cpp但是假如该目录下只有头文件&#xff0c;没…...

ACM模式常用方法总结(Java篇)

文章目录 一、ACM输入输出模式二、重要语法2.1、导包2.2、读取数据2.3、判断是否有下一个数据2.4、输出2.5、关闭scanner2.6、易踩坑点 一、ACM输入输出模式 在力扣上编写代码时使用的是核心代码模式&#xff0c;如果在面试中遇到ACM模式就会比较迷茫&#xff1f;ACM模式要求你…...

日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习(3号通知)

日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习&#xff08;3号通知&#xff09; 日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习&#xff08;3号通知&#xff09;...

leetcode 28 Find the Index of the First Occurrence in a String

直接用kmp算法 class Solution { public:int strStr(string haystack, string needle) {return kmp(haystack,needle);}int kmp(std::string &text,std::string &pattern){int n text.size();int m pattern.size();if(m 0)return 0;std::vector<int> next;ne…...

MATLAB中rmfield函数用法

目录 语法 说明 示例 删除单个字段 删除多个字段 rmfield函数的功能是删除结构体中的字段。 语法 s rmfield(s,field) 说明 s rmfield(s,field) 从结构体数组 s 中删除指定的一个或多个字段。使用字符向量元胞数组或字符串数组指定多个字段。s 的维度保持不变。 示例…...

Linux C语言调用第三方库,第三方库如何编译安装

在 Linux 环境下使用 C 语言调用第三方库时&#xff0c;通常需要先对第三方库进行编译和安装。以下为你详细介绍一般的编译安装步骤&#xff0c;并给出不同类型第三方库&#xff08;如使用 Makefile、CMake 构建系统&#xff09;的具体示例。 一般步骤 1. 获取第三方库源码 …...

leetcode -编辑距离

为了求解将 word1 转换成 word2 所需的最少操作数&#xff0c;可以使用动态规划。以下是详细的解决方案&#xff1a; ### 方法思路 1. **定义状态** dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最少操作数。 2. **状态转移方程** - 如果 word1[…...

【Ollama】大模型运行框架

文章目录 安装与运行导入LLMHugginface模型-转换为-GGUF模型在指定gpu上运行model存储路径设置 ollama接口 官网 github中文介绍 安装与运行 安装教程 安装 wget https://ollama.com/download/ollama-linux-amd64.tgz tar -xzvf ollama-linux-amd64.tgz添加ollama的环境变量…...

字节开源版Manus来袭

字节开源版Manus来袭 项目地址&#xff1a;https://github.com/langmanus/langmanus/blob/main/README_zh.md 在人工智能领域&#xff0c;Manus的出现无疑是一颗重磅炸弹&#xff0c;它凭借强大的通用Agent能力&#xff0c;迅速吸引了全球开发者和AI爱好者的目光。然而&#…...

论文阅读笔记——PointVLA: Injecting the 3D World into Vision-Language-Action Models

PointVLA 论文 现有的 VLA 基于 2D 视觉-语言数据表现良好但缺乏 3D 几何先验导致空间推理缺陷。传统方案&#xff1a;1&#xff09;3D->2D 投影&#xff0c;造成几何信息损失&#xff1b;2&#xff09;3D 数据集少。PointVLA 保留原有 VLA&#xff0c;提取点云特征&#xf…...

selenium实现自动登录项目(5)

1、163邮箱自动登录功能 遇到的问题&#xff1a; 1、登录页面&#xff0c;在定位表单时候&#xff0c;采用id&#xff0c;xpath&#xff0c;css selector都无法定位成功&#xff0c;因为id后面有个随机生成的数字&#xff08;//*[id"x-URS-iframe1741925838640.6785&quo…...