go语言 | 图解反射(二)

news2024/9/20 5:40:13

在这里插入图片描述

reflect.Value 和 reflect.Type

反射有两种类型 reflect.Value 和 reflect.Type ,分别表示变量的值和类型,并且提供了两个函数 reflect.ValueOf 和 reflect.TypeOf 分别获取任意对象的 reflect.Value 和 reflect.Type。


reflect.Value

reflect.Value 可以通过函数 reflect.ValueOf 获得。reflect.Value 被定义为一个 struct 结构体:

type Value struct {
   typ *rtype
   ptr unsafe.Pointer
   flag
}

typ: 用于保存值的类型信息。在 Go 的 reflect 包中,rtype 结构表示类型信息。

ptr: 用于保存数据的指针或指向数据的指针。如果 flagIndir 标志被设置,ptr 就是指向数据的指针;否则,ptr 就是数据的指针。

flag: 用于保存与值相关的元信息和标志位。flag 的最低的几位用于表示标志位,其余的位用于存储类型的信息和其他元数据。flag 的位域包括:

  • flagStickyRO: 表示该值是通过不导出的非嵌入字段获得的,因此是只读的。
  • flagEmbedRO: 表示该值是通过导出的嵌入字段获得的,因此是只读的。
  • flagIndir: 标志 val 持有指向数据的指针。
  • flagAddr: v.CanAddr 为 true,表示该值的地址可以被取得(implies flagIndir)。
  • flagMethod: 表示该值是一个方法值。
  • 低几位表示值的种类(Kind)。
  • 其余的位用于存储方法值的方法号。

在这里插入图片描述

可以看到,这个结构体中的字段都是私有的,我们只能使用 reflect.Value 的方法,部分方法比如有:

在这里插入图片描述
方法分为三类:

  • 获取和修改对应的值
  • struct 类型的字段有关,用于获取对应的字段
  • 类型上的方法集有关,用于获取对应的方法

任意类型的对象 与 reflect.Value 互转

func main() {
   i := 5
   //int to reflect.Value
   iv:=reflect.ValueOf(i)
   //reflect.Value to int
   i1 := iv.Interface().(int)
   fmt.Println(i1)
}
//运行结果:
5
  • i := 5:创建一个整数变量 i 并初始化为 5。
  • iv := reflect.ValueOf(i):使用 reflect.ValueOf 函数将整数 i 转换为 reflect.Value 类型。iv 现在是一个包含整数值的 reflect.Value 对象。
  • i1 := iv.Interface().(int):使用 Interface() 方法将 reflect.Value 转换为接口类型,并通过断言将其还原为原始的整数类型。这里假设 iv 确实包含一个整数值,因此使用 (int) 进行断言。如果类型不匹配,这会导致运行时恐慌。
  • fmt.Println(i1):打印还原后的整数值。

修改对应的值

func main() {
   i := 5
   ipv := reflect.ValueOf(&i)
   ipv.Elem().SetInt(6)
   fmt.Println(i)
}

//运行结果:
6
  • 示例中我们通过反射修改了一个变量。
  • reflect.ValueOf 函数返回的是一份值的拷贝,所以我们要传入变量的指针才可以。
  • 因为传递的是一个指针,所以需要调用 Elem 方法找到这个指针指向的值,这样才能修改。
  • 要修改一个变量的值,关键点:传递指针(可寻址),通过 Elem 方法获取指向的值,才可以保证值可以被修改,reflect.Value 为我们提供了 CanSet 方法判断是否可以修改该变量。

修改 struct 结构体字段的值

func main() {
   p := person{Name: "微客鸟窝",Age: 18}
   pv:=reflect.ValueOf(&p)
   pv.Elem().Field(0).SetString("无尘")
   fmt.Println(p)
}
type person struct {
   Name string
   Age int
}

//运行结果:
{无尘 18}

步骤总结:

  • 传递一个 struct 结构体的指针,获取对应的 reflect.Value;
  • 通过 Elem 方法获取指针指向的值;Elem 方法是在 Go 中 reflect.Value 类型上的一个方法,它用于获取接口或指针类型 reflect.Value 中所包含的值。如果 v 是一个指向某个值的指针,Elem 方法会返回该指针指向的值;如果 v 是一个接口类型,Elem 方法会返回接口中包含的具体值。
  • 通过 Field 方法获取要修改的字段;

通过反射修改一个值的规则:
可被寻址,通俗地讲就是要向 reflect.ValueOf 函数传递一个指针作为参数。
如果要修改 struct 结构体字段值的话,该字段需要是可导出的,而不是私有的,也就是该字段的首字母为大写。
记得使用 Elem 方法获得指针指向的值,这样才能调用 Set 系列方法进行修改。

获取对应的底层类型

因为我们可以通过 type 关键字来自定义一些新的类型,而底层类型就是一些基础类型。比如上面示例中的 p 变量类型为 person,底层类型为 struct 结构体类型。

type person struct {
   Name string
   Age int
}

func main() {
	p := person{Name: "微客鸟窝", Age: 18}
	p1 := reflect.ValueOf(p)
	p2 := reflect.ValueOf(&p)
	fmt.Println(p1.Kind())
	fmt.Println(p2.Kind())
}

//运行结果:
struct
ptr

Kind 方法返回一个 Kind 类型的值,它是一个常量,从源码看下定义的 Kind 常量列表,含了 Go 语言的所有底层类型:

在这里插入图片描述
一共 26 种,我们可以分类如下:

  • 基础类型Bool、String以及各种数值类型(有符号整数Int/Int8/Int16/Int32/Int64,无符号整数Uint/Uint8/Uint16/Uint32/Uint64/Uintptr,浮点数Float32/Float64,复数Complex64/Complex128)
  • 复合(聚合)类型Array和Struct
  • 引用类型Chan、Func、Ptr、Slice和Map(值类型和引用类型区分不明显,这里不引战,大家理解意思就行)
  • 接口类型Interface
  • 非法类型Invalid,表示它还没有任何值(reflect.Value的零值就是Invalid类型)

Field和NumField

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name    string
	Age     int
	Address string
}

func main() {
	p := Person{"Alice", 30, "123 Main St"}

	// 使用 reflect.ValueOf 获取结构体实例的反射值
	pValue := reflect.ValueOf(p)

	// 获取结构体字段的个数
	numFields := pValue.NumField()
	fmt.Println("Number of fields:", numFields)

	// 遍历结构体的字段
	for i := 0; i < numFields; i++ {
		// 获取第 i 个字段的反射值
		fieldValue := pValue.Field(i)

		// 输出字段名和值
		fieldName := pValue.Type().Field(i).Name
		fmt.Printf("Field %s: %v\n", fieldName, fieldValue.Interface())
	}
}

//输出结果
Number of fields: 3
Field Name: Alice
Field Age: 30
Field Address: 123 Main St

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

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

相关文章

Using Definition View 使用定义视图

You use Definition view to create definitions within a defined hierarchical structure, in which nodes represent the definitions. A node is the visual representation of a section, step, or action that you can select, collapse,modify, and so on. 您可以使用“…

JVM虚拟机——类加载器(JDK8及以前,打破双亲委派机制)(JDK9之后的类加载器)

目录 1.自定义类加载器2.线程上下文类加载器3.OSGi模块化4.JDK9之后的类加载器5.类加载器总结 1.自定义类加载器 ⚫ 一个Tomcat程序中是可以运行多个Web应用的&#xff0c;如果这两个应用中出现了相同限定名的类&#xff0c;比如Servlet类&#xff0c;Tomcat要保证这两个类都能…

nestJs(三) 数据库

真正的服务往往包括数据存储。 本篇将介绍如何建立 NestJs 的数据库连接、并使用数据库联表查询。这样就就是完整的后台服务了。 开发准备 下载并安装 Mysql创建 school 库 create database school;3.安装 nestjs/typeorm typeorm mysql2 npm install --save nestjs/typeor…

医学图像 开源数据整理合集1

本文为医学图像 开源数据整理合集&#xff0c;为科研数据提供方便和检索。 目录 1 NIH database of 100000 Chest X-rays 2 The Cancer Imaging Archive (TCIA) 3 National Institute for Mental Healths (NIMHs) OpenNeuro.org 4 RSNAs Quantitative Imaging Data Wareh…

css技巧分享(优惠券缺角样式实现)

主要知识点&#xff1a;radial-gradient radial-gradient() CSS 函数创建一个图像&#xff0c;该图像由从原点辐射的两种或多种颜色之间的渐进过渡组成。它的形状可以是圆形或椭圆形。函数的结果是 数据类型的对象。这是一种特别的 。 .coupon{width: 190rpx;height: 194rpx;b…

QGIS之二十栅格数据定义投影

效果 步骤 1、准备数据 2、定义投影 Qgis工具箱中搜索“投影” 指定投影坐标系&#xff0c;例如EPSG&#xff1a;4549 运行 3、结果 查看属性

炫酷爱心表白

一、代码 <!DOCTYPE html> <!-- saved from url(0051)https://httishere.gitee.io/notion/v4/love-name.html --> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>&#x1f4…

Linux系统进程与进程间通信

Linux是一个多用户、多任务的操作系统&#xff0c;支持多个进程同时运行。进程是Linux系统中的基本单元&#xff0c;它们负责执行各种任务&#xff0c;如网页浏览、文件下载、程序运行等。在Linux中&#xff0c;进程是由一个或多个线程组成的&#xff0c;线程是进程的基本执行单…

VSCode配置MingW编译调试环境

1.MingW简介 MinGW&#xff0c;即 Minimalist GNU For Windows。它是一些头文件和端口库的集合&#xff0c;该集合允许人们在没有第三方动态链接库的情况下使用 GCC&#xff08;GNU Compiler C&#xff09;产生 Windows32 程序。 实际上 MinGW 并不是一个 C/C 编译器&#xf…

92 [递归实现指数型枚举](https://www.acwing.com/problem/content/94/)

从 1∼n1∼&#xfffd; 这 n&#xfffd; 个整数中随机选取任意多个&#xff0c;输出所有可能的选择方案。 输入格式 输入一个整数 n&#xfffd;。 输出格式 每行输出一种方案。 同一行内的数必须升序排列&#xff0c;相邻两个数用恰好 11 个空格隔开。 对于没有选任何…

Spring6(二):IoC容器

文章目录 3. 容器&#xff1a;IoC3.1 IoC容器3.1.1 控制反转&#xff08;IoC&#xff09;3.1.2 依赖注入3.1.3 IoC容器在Spring的实现 3.2 基于XML管理Bean3.2.1 搭建子模块spring6-ioc-xml3.2.2 获取bean方式一&#xff1a;根据id获取方式二&#xff1a;根据类型获取方式三&am…

大语言模型|人工智能领域中备受关注的技术

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️其他领域】 文章目录 前言关于大语言模型大语言模型是什么&#xff1f;大语言模型有什么用?文案写作知识库回答文本分类代码生成 AWS 如何通过 LLM 提供帮助&#xff1f;Amazon BedrockAmazon SageM…

anaconda中安装pytorch和TensorFlow环境并在不同环境中安装kernel

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

unity使用vs进行c#代码提示,查看F12unity元代码

unity关联vs 在vs中让cs.meta显示&#xff0c;鼠标右键&#xff0c;包含在内 提示GameObject类了 感谢下面这位的提示https://zhuanlan.zhihu.com/p/551119106

概率论和数理统计(四)方差分析与回归分析

前言 实际场景中,也需要研究两个变量的关系.检验也可能出现两个以上的总体. 方差分析 假设检验中&#xff0c;若需检验 H 0 : μ 1 μ 2 &#xff0c; H 1 : μ 1 ̸ μ 2 H_0:μ_1μ_2&#xff0c;H_1:μ_1 \notμ_2 H0​:μ1​μ2​&#xff0c;H1​:μ1​μ2​&#x…

MySQL(18):MySQL8.0的其它新特性

MySQL从5.7版本直接跳跃发布了8.0版本。 MySQL8.0 新增特征 1.更简便的NoSQL支持。 NoSQL泛指非关系型数据库和数据存储。随着互联网平台的规模飞速发展&#xff0c;传统的关系型数据库已经越来越不能满足需求。从5.6版本开始&#xff0c;MySQL就开始支持简单的NoSQL存储功能…

【计算机组成原理】绘制出纯整数(1字节)和纯小数的数轴

绘制出用原码、反码、补码表示纯整数(字节) 的数轴&#xff1a; 对于一字节的大小&#xff0c;原码和反码都只能表示255个数字&#xff0c;因为0占了2个符号数。而补码能表示256个数字&#xff0c;因为0和-0的补码是一样的。所以多出来一个符号数1000 0000能够表示-128所以&…

AMEYA360分析:炬玄智能高精准度、低相噪TCXO时钟补偿芯片

炬玄智能一款TCXO芯片JXT171和生产补偿系统成功通过应用测试&#xff0c;指标达到国际先进水平&#xff0c;实现该产品品类国内首家全国产化突破&#xff0c;为重点行业终端客户供应链保障续上关键一环。 1、典型应用 随着移动通信技术在我国得到广泛应用&#xff0c;蓬勃发展的…

程序员的护城河:技术深度、创新精神与软实力的完美结合

文章目录 1. 技术深度&#xff1a;建立坚实的技术基石2. 创新精神&#xff1a;应对变革的利器3. 软实力&#xff1a;沟通协作构筑团队防线4. 结合三者构筑完美护城河 &#x1f389;程序员的护城河&#xff1a;技术深度、创新精神与软实力的完美结合 ☆* o(≧▽≦)o *☆嗨~我是I…

东南亚电商平台,如何有效防范欺诈商户入驻?

目录 柬埔寨居民频遭电商欺诈 平台如何防范欺诈商家入驻 柬埔寨电信监管机构最新公布的数据显示&#xff0c;截至2022年1月&#xff0c;柬埔寨移动电话用户数量已达到19,458,849人&#xff0c;互联网用户数量达到1,7591,396人。这一数据表明&#xff0c;柬埔寨的数字化趋势日…