Golang 反射

news/2024/7/27 8:00:44/文章来源:https://blog.csdn.net/e5pool/article/details/135523159

前言

反射(reflection)是一种能够检查程序在运行时的变量类型和值的机制。Go的反射机制定义在reflect包中。使用反射,可以动态地调用对象的方法或访问其字段,即使在编写代码时并不知道这些方法或字段的具体存在

反射主要涉及到reflect.Typereflect.Value这两个类型。reflect.Type代表Go值的类型,而reflect.Value代表Go值的具体值

在使用反射时需要注意的是,反射操作通常比直接操作要慢,因此在性能敏感的代码中应当谨慎使用反射。另外,过度使用反射可能会使代码难以理解和维护

获取值

基本步骤是通过reflect.ValueOf()函数获取反射值对象,然后使用该对象的方法来获取具体的值

package mainimport ("fmt""reflect"
)func main() {var (x float64 = 11.2y int     = 55z string  = "hello")// 获取x的反射值对象valueOfX := reflect.ValueOf(x)// 获取实际的float64值fmt.Println("value of x:", valueOfX.Float())// 获取y的反射值对象valueOfY := reflect.ValueOf(y)// 获取实际的int值fmt.Println("value of y:", valueOfY.Int())// 获取z的反射值对象valueOfZ := reflect.ValueOf(z)// 获取实际的string值fmt.Println("value of z:", valueOfZ.String())
}

创建了三个变量x、y和z,分别是float64、int和string类型。对于每个变量,用reflect.ValueOf()获取其反射值对象,并根据变量的基本类型,使用相应的方法来获取实际的值。例如,对于float64类型的变量,我们调用Float()方法来获取它的值。类似地,对于int和string,我们分别调用Int()和String()方法

反射值对象reflect.Value提供了一系列的方法来获取具体类型的值,包括但不限于Bool()Bytes()Complex()Float()Int()String()等。每个方法对应于原始值的类型,尝试获取与值类型不匹配的类型会导致运行时panic

修改值

使用reflect包可以修改反射对象的值,须满足以下条件:

  • 反射对象reflect.Value必须是可寻址的(可设置的)
  • 反射对象对应的值必须是可导出的(即字段名首字母大写)

要修改一个变量的值,需要通过reflect.ValueOf获得反射值对象,并通过.Elem()方法获取指针指向的值的反射值对象

package mainimport ("fmt""reflect"
)func main() {var x = 2.7fmt.Println("before:", x)// 获取变量x的反射值对象p := reflect.ValueOf(&x) // 注意:这里传入的是x的地址// 获取反射值对象对应的值(需要是可设置的)v := p.Elem()// 判断反射值对象是否可以改变if v.CanSet() {// 修改反射值对象的值v.SetFloat(11.3)}fmt.Println("after:", x)
}

首先获取x的地址的反射对象p,然后通过调用p.Elem()获取x值的反射对象v。由于v是通过指针获取的,所以它是可寻址的,然后通过v.SetFloat来修改x的值

reflect.Type()reflect.Kind() 的使用

reflect.Type 表示一个Go值的具体类型,包括其名称和包路径。这个类型是接口类型,提供了关于Go值的详细类型信息,比如结构体的字段信息、一个函数的签名等

reflect.Kind 则表示一个Go值的基本分类,或者说是其底层类型。在Go语言中,基本分类包括如intfloat64boolstructslice 等。每一个 reflect.Type 都对应一个 reflect.Kind,但是不同的 reflect.Type 可能对应相同的 reflect.Kind。例如,所有结构体类型的 reflect.Kind 都是 reflect.Struct

举例说明

type MyStruct struct {Field1 intField2 string
}s := MyStruct{Field1: 10, Field2: "Hello"}
v := reflect.ValueOf(s)fmt.Println("type:", v.Type()) // 输出s的具体类型,例如 "main.MyStruct"
fmt.Println("kind:", v.Kind()) // 输出s的基本分类,例如 "struct"

结构体中的反射

  1. 获取值
    获取到反射值对象后,可以是通过.Field()拿到元数据,然后点其中的方法,拿到字段属性
package mainimport ("fmt""reflect"
)type User struct {ID     int    `json:"id"`Name   string `json:"name"`Age    int    `json:"age,omitempty"`weight float64
}func GetUser(user User) {t := reflect.TypeOf(user)           // 获取user变量的类型对象v := reflect.ValueOf(user)          // 获取user变量的值对象for i := 0; i < v.NumField(); i++ { // 以结构体字段个数,遍历f := t.Field(i) // 获取第i个字段的元数据, 以第二个字段为例:{Name  string json:"name" 8 [1] false}fmt.Println(f.Name, f.Type, f.Tag, f.Offset, f.Index, f.Anonymous, f.PkgPath)  // 下面解释提到fmt.Printf("%#v\n", f.Tag.Get("json")) // 打印字段的json标签值fmt.Println(v.Field(i))                // 获取第i个字段的值fmt.Println("----------------------------")}}func main() {user := User{ID:     1,Name:   "张三",Age:    20,weight: 45.5,}GetUser(user)
}

运行后,控制台输出:

ID int json:"id" 0 [0] false
"id"
1
----------------------------
Name string json:"name" 8 [1] false
"name"
张三
----------------------------
Age int json:"age,omitempty" 24 [2] false
"age,omitempty"
20
----------------------------
weight float64  32 [3] false main
""
45.5
----------------------------

字段元数据中的其他字段属性解释:

  • f.Name: 字段名称,例如 "Name"
  • f.Type: 字段的类型,例如 reflect.TypeOf("") 表示 string 类型
  • f.Tag: 字段的标签(Tag),是结构体中字段后定义的额外信息,例如 json:"name"
  • f.Offset: 字段在结构体内存中的偏移量,表示该字段在结构体中的起始字节位置。
  • f.Index: 字段的索引切片,表示嵌套字段的访问索引。对于顶层字段,这将是单个元素的切片,例如 [1]
  • f.Anonymous: 表示字段是否是匿名字段(即嵌入字段),布尔值 truefalse
  • f.PkgPath: 如果字段是非导出的(即小写开头),则为字段的包路径。如果字段是导出的(即大写开头),它将是空字符串。我的例子中,就是main
  1. 再来看看修改值
    首先&传入结构体指针,然后.Elem()获取指针指向的值的反射值对象,再然后就同取值一样,.Field()之类的,最后修改就是.SetInt.SetFloatSetString 等这些,同普通类型的获取值基本一致
package mainfunc SetUser(user *User) { // 或者用 any 来替代*User,但应处理异常v := reflect.ValueOf(user).Elem()v.FieldByName("Age").SetInt(22)
}
func main() {user := User{ID:     1,Name:   "张三",Age:    20,weight: 45.5,}SetUser(&user)fmt.Println(user.Age)  // 22}

这里需要注意:设置的字段需要是可导出的(大写)且可寻址的,否则会引发 panic

  1. 反射调用结构体的方法
    经过前面学习,我们知道字段是通过.Field()之类的方法来找到的,同样地,方法是通过.Method()之类的方法来找到
    调用结构体方法,是通过.Call(in []Value) []Value来实现
package mainimport ("fmt""reflect"
)type MyStruct struct {Value int
}// Add 是一个指针接收器方法
func (s *MyStruct) Add(amount int) {s.Value += amount
}func main() {// 创建结构体实例instance := MyStruct{Value: 100}// 获取reflect.Value类型的实例val := reflect.ValueOf(&instance)// 寻找方法method := val.MethodByName("Add")// 检查方法是否存在if !method.IsValid() {fmt.Println("Method not found.")return}// 参数必须作为reflect.Value的切片传入args := []reflect.Value{reflect.ValueOf(20)}// 调用方法method.Call(args)// 输出结果fmt.Println(instance.Value)  // 120
}

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

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

相关文章

Go 知多少?

作为一名已接触过其他语言的开发&#xff0c;再去学习一门新语言可比之前轻松不少&#xff0c; 语言之间存在很多相似点&#xff0c;但是新语言也有自己的不同点&#xff0c;通常我会先了解它与其他语言常遇到的不同点有哪些&#xff0c; 使自己先能够上手编写基础程序&#…

深入理解 Spark(一)spark 运行模式简介与启动流程源码分析

spark 的运行模式 standalone 模式 以 standalone-client 为例&#xff0c;运行过程如下&#xff1a; SparkContext 连接到 Master&#xff0c;向 Master 注册并申请资源&#xff08;CPU Core 和 Memory&#xff09;&#xff1b;Master 根据 SparkContext 的资源申请要求和 …

ruoyi后台管理系统部署-4-安装nginx

yum 安装 ngix 1.24 yum 官方源安装&#xff1a; # 1. 需要预先安装 yum-utils sudo yum install yum-utils # 2. 配置yum repo touch /etc/yum.repos.d/nginx.repongix.repo: [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$…

ETF交易好不好?如何选择一个好的ETF基金?

作为普通投资者&#xff0c;交易ETF基金时&#xff0c;拥有证券账户即可&#xff0c;如同买卖股票一样操作&#xff0c;非常便捷&#xff0c;可以在盘中实时买卖。和投资股票相比较&#xff0c;风险相对较低。 首先&#xff0c;什么是ETF基金? ETF其实是一个缩写&#xff1a…

41k+ stars 闪电般快速的开源搜索引擎 docker安装教程

目录 1.下载 2.启动 成功示例 3.创建索引 4.插入数据 4.1下载数据 4.2插入数据 4.3查看数据 5.官方地址 1.下载 docker pull getmeili/meilisearch:latest 2.启动 mkdir -p /opt/meili_datadocker run -it --rm \-p 7700:7700 \-v /opt/meili_data:/meili_data \ge…

AI绘画学起来真的那么难吗?附全网最全教程!!

手把手教你入门绘图超强的AI绘画&#xff0c;用户只需要输入一段图片的文字描述&#xff0c;即可生成精美的绘画。给大家带来了全新保姆级教程资料包 &#xff08;文末可获取&#xff09; 随着2022年末ChatGPT的爆火&#xff0c;人们终于不能再忽视AI的强大了&#xff0c;AI绘…

Android studio RecyclerView 应用设计

一、创建empty activity项目: 二、打开activity_main.xml布局文件: 添加RecyclerView控件 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/…

浅谈对Maven的理解

一、什么是Maven Maven——是Java社区事实标准的项目管理工具&#xff0c;能帮你从琐碎的手工劳动中解脱出来&#xff0c;帮你规范整个组织的构建系统。不仅如此&#xff0c;它还有依赖管理、自动生成项目站点等特性&#xff0c;已经有无数的开源项目使用它来构建项目并促进团队…

【LabVIEW FPGA 编程入门】使用FPGA IO进行编程

1.在项目中新建一个VI&#xff0c;命名为FPGA IO Test。 2. 可以直接将项目中的FPGA IO拖入程序框图中。 FPGA IO的类型&#xff1a; 数字线&#xff1a; 数字端口&#xff1a; 模拟IO&#xff1a; 其他&#xff1a; 3.如果新增加了FPGA资源&#xff0c;不是创建项目时扫描到的…

C++算法学习心得五.二叉树(3)

1.合并二叉树&#xff08;617题&#xff09; 题目要求&#xff1a; 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们…

用Python做一个2048小游戏

文章目录 逻辑设计绘图逻辑主循环 逻辑设计 2048的逻辑无非是操作 4 4 4\times4 44的方格&#xff0c;每个方格中有一个数&#xff0c;这些数可以移动&#xff0c;如果两个相同的数字在移动时相撞了&#xff0c;就可以彼此合并。 而这个 4 4 4\times4 44的方格&#xff0c;…

Jenkins自动化部署docker

Jenkins自动化部署docker和普通方式构建 docker外挂目录 准备测试服务器docker环境准备jdk环境将上传jar包修改为app.jar对外暴露1000端口启动jar FROM openjdk:8-jdk-alpine ARG JAR_FILE COPY ${JAR_FILE} app.jar EXPOSE 1000 ENTRYPOINT ["java","-jar&q…

搭建知识付费小程序平台:如何避免被坑,选择最佳方案?

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

(学习日记)2024.01.09

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

CSS 下载进度条

<template><view class=btn>下载中</view></template><script></script><style>/* 设置整个页面的样式 */body {width: 100vw; /* 页面宽度为视口宽度 */background: #000000; /* 背景颜色为白色 */display: flex; /* 使用 flex…

领导看了就给我升职加薪的年终总结,我只花5分钟就写完了!

年末正是做总结的时候&#xff0c;如果你正苦于年终总结不知道该如何下手&#xff0c;ProcessOn刚好上线了AI一键帮你生成年终总结的活动&#xff0c;还可以下载成PPT格式&#xff0c;或许可以助你一臂之力&#xff0c;操作起来十分简单。当然&#xff0c;计划也可以一键生成&a…

Linux 内核大转变:是否将迈入现代 C++ 的时代?

Linux开发者 H. Peter Anvin 在邮件列表中重启了关于 Linux内核C代码转换为C的讨论&#xff0c;并陈述了自己的观点。说之前先看一下这个话题的历史背景。 早在2018年4月1日&#xff0c;Andrew Pinski提议将 Linux 内核源码转为 C&#xff0c;在文中写道之所以引入是由于以下优…

2023 年公链发展报告

作者&#xff1a;stellafootprint.network 2023 年&#xff0c;公链领域展现出强大的韧性和持续的创新力。这一年&#xff0c;比特币的强势回归、以太坊的稳步增长以及 Solana 的惊人崛起&#xff0c;共同绘制出一幅市场复苏的生动画面。在这一背景下&#xff0c;公链加密货币…

Springboot + websocket 实现 一对一 单人聊天

Springboot websocket 实现 一对一 单人聊天 要使用websocket ,需要添加 jar 打开项目中的pom.xml,添加以下内容 创建java端代码 配置websocke的endpoints 配置websocket的server ServerEndpoint(value "/websocket/{username}") 这句话 一定要注意, 这里 路…

计算机毕业设计 基于Java的国产动漫网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…