golang学习笔记27-反射【重要】

news2024/9/30 12:56:22

本节也是GO核心部分,很重要。包括基本类型的反射,结构体类型的反射,类别方法Kind(),修改变量的值。

目录

    • 一、概念,基本类型的反射
    • 二、结构体类型的反射
    • 三、类别方法Kind()
    • 四、修改变量的值

一、概念,基本类型的反射

【1】反射可以做什么?
1)反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别等信息
2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
3)通过反射,可以修改变量的值,可以调用关联的方法。
4)使用反射,需要import "reflect"
【2】反射相关的函数
1)reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
2)reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型
反射不仅可以获取变量名和变量类型,reflect.Type也可以通过空接口转回原类型:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 定义一个变量
	var x int = 42

	// 获取变量的类型
	t := reflect.TypeOf(x)
	fmt.Println("Type:", t) // 输出: Type: int

	// 获取变量的值
	v := reflect.ValueOf(x)
	fmt.Println("Value:", v) // 输出: Value: 42

	// 将 reflect.Value 转换回原始类型
	// Step 1: 将 reflect.Value 转换为 empty interface (interface{})
	emptyInterface := v.Interface() // 这里使用空接口可以接受任何类型的值
	// Step 2: 使用类型断言将 empty interface 转换回原始类型 int
	originalValue := emptyInterface.(int)         // 将空接口断言为 int 类型
	fmt.Println("Original value:", originalValue) // 输出: Original value: 42
}

反射和数据类型互转的流程图如下:
在这里插入图片描述

二、结构体类型的反射

和基本类型的情况差不多,但要注意因为实现接口的结构体可能有多个,接口转结构体要判断是否转成功:

package main

import (
	"fmt"
	"reflect"
)

// 定义 student 结构体
type student struct {
	Name string
	Age  int
}

func main() {
	// 创建一个 student 实例
	s := student{Name: "Alice", Age: 20}

	// 获取变量的类型
	t := reflect.TypeOf(s)
	fmt.Println("类型:", t) // 输出: 类型: main.student

	// 获取变量的值
	v := reflect.ValueOf(s)
	fmt.Println("值:", v) // 输出: 值: {Alice 20}

	// 将 reflect.Value 转换回原始类型
	// Step 1: 将 reflect.Value 转换为 empty interface (interface{})
	emptyInterface := v.Interface() // 这里使用空接口可以接受任何类型的值

	// Step 2: 使用类型断言将 empty interface 转换回原始类型 student
	originalStudent, ok := emptyInterface.(student) // 将空接口断言为 student 类型
	if ok {
		// 如果转换成功,输出姓名和年龄
		fmt.Printf("原始学生 - 姓名: %s, 年龄: %d\n", originalStudent.Name, originalStudent.Age) // 输出: 原始学生 - 姓名: Alice, 年龄: 20
	} else {
		fmt.Println("类型断言为 student 失败。")
	}
}

三、类别方法Kind()

Kind()是reflect.Type的一个方法,用于获取类型的基本种类(kind)。它返回一个reflect.Kind类型的值,用于描述基本数据类型的特性,如int、string、struct等。
Kind()和TypeOf()的区别如下表所示:

特性reflect.TypeOf()reflect.Kind()
返回值返回 reflect.Type 类型的对象返回 reflect.Kind 类型的枚举值
作用获取变量的完整类型信息获取变量的基本种类(如 intstringstruct
适用场景当需要获取类型的详细信息时当只需要判断数据类型的基本特性时

语法:TypeOf(s).Kind()ValueOf(s).Kind(),这两个操作都返回变量s的基本类型。

四、修改变量的值

如果用反射修改x的类型,需要先获取reflect.Value类型,然后用对应x类型的方法,比如SetInt(),如果x是int*,则需要先用Elem(),再用SetInt():

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x int = 42
	p := &x // 创建指向 x 的指针

	// 获取指针的 reflect.Value
	v := reflect.ValueOf(p)

	// 使用 Elem() 获取指针指向的值
	elem := v.Elem()

	// 修改指针指向的值
	elem.SetInt(100)

	// 输出修改后的值
	fmt.Println("修改后的值:", x) // 输出: 修改后的值: 100
}

如果x是结构体,要用Field()获取字段,Method()获取方法,用reflect.Value切片调用有参方法,用nil调用无参方法:

package main

import (
	"fmt"
	"reflect"
)

// 定义 student 结构体
type student struct {
	Name string
	Age  int
}

// 为 student 结构体定义一个方法
func (s *student) SetAge(age int) {
	s.Age = age
}

// 为 student 结构体定义另一个方法
func (s *student) GetInfo() string {
	return fmt.Sprintf("姓名: %s, 年龄: %d", s.Name, s.Age)
}

func main() {
	// 创建一个 student 实例
	s := student{Name: "Alice", Age: 20}

	// 获取结构体的类型,使用指针获取
	stuType := reflect.TypeOf(&s)

	// 获取字段数量
	numFields := stuType.Elem().NumField() // 使用 Elem() 获取底层类型
	fmt.Printf("字段数量: %d\n", numFields)

	// 遍历字段
	for i := 0; i < numFields; i++ {
		field := stuType.Elem().Field(i) // 使用 Elem() 获取底层类型的字段
		fmt.Printf("字段名: %s, 字段类型: %s\n", field.Name, field.Type)
	}

	// 获取方法数量
	numMethods := stuType.NumMethod() // 获取方法数量
	fmt.Printf("方法数量: %d\n", numMethods)

	// 遍历方法
	for i := 0; i < numMethods; i++ {
		method := stuType.Method(i)
		fmt.Printf("方法名: %s\n", method.Name)
	}

	// 使用反射修改 Name 字段的值
	stuValue := reflect.ValueOf(&s)       // 获取结构体的反射值,使用指针可以修改值
	nameField := stuValue.Elem().Field(0) // 获取第一个字段的反射值

	// 确保字段可设置
	if nameField.CanSet() {
		nameField.SetString("Bob") // 修改 Name 字段的值为 "Bob"
	}

	// 调用 SetAge 方法,将年龄设置为 30
	setAgeMethod := stuValue.MethodByName("SetAge")
	args := []reflect.Value{reflect.ValueOf(30)} // 创建包含参数的切片
	setAgeMethod.Call(args)                      // 调用 SetAge 方法,传入参数

	// 调用 GetInfo 方法
	getInfoMethod := stuValue.MethodByName("GetInfo")
	info := getInfoMethod.Call(nil) // 调用方法,传递空参数

	// 输出信息
	fmt.Println(info[0]) // 输出: 姓名: Bob, 年龄: 30
}

关键代码解释:
1.info := getInfoMethod.Call(nil)
infoMethod是通过反射获取到的一个方法的反射值。在这个例子中,它指向student结构体的Info方法。Call是reflect.Value类型的方法,用于调用一个方法。它接受一个参数,参数是一个reflect.Value切片,表示要传递给被调用方法的参数。在这里,我们传递了nil,表示Info方法不需要任何参数。在这个例子中,GetInfo方法返回一个字符串,因此info将是一个包含一个reflect.Value的切片,表示学生信息字符串。
2. args := []reflect.Value{reflect.ValueOf(30)}
这一行创建了一个reflect.Value切片,命名为args,它将用于调用SetAge方法。reflect.ValueOf(30)用于将整数30转换为reflect.Value类型。[]reflect.Value{}表示创建一个reflect.Value类型的切片,作为SetAge方法的参数。

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

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

相关文章

Linux云计算 |【第四阶段】RDBMS1-DAY3

主要内容&#xff1a; 子查询&#xff08;单行单列、多行单列、单行多列、多行多列&#xff09;、分页查询limit、联合查询union、插入语句、修改语句、删除语句 一、子查询 子查询就是指的在一个完整的查询语句之中&#xff0c;嵌套若干个不同功能的小查询&#xff0c;从而一…

STM32 OLED

文章目录 前言一、OLED是什么&#xff1f;二、使用步骤1.复制 OLED.C .H文件1.1 遇到问题 2.统一风格3.主函数引用头文件3.1 oled.h 提供了什么函数 4.介绍显示一个字符的函数5. 显示十进制函数的讲解 三、使用注意事项3.1 配置符合自己的引脚3.2 花屏总结 前言 提示&#xff…

第L2周:机器学习|线性回归模型 LinearRegression:2. 多元线性回归模型

本文为365天深度学习训练营 中的学习记录博客原作者&#xff1a;K同学啊 任务&#xff1a; ●1. 学习本文的多元线形回归模型。 ●2. 参考文本预测花瓣宽度的方法&#xff0c;选用其他三个变量来预测花瓣长度。 一、多元线性回归 简单线性回归&#xff1a;影响 Y 的因素唯一&…

python15_转换为ASCII

转换为ASCII A A B 你好 C 66def str_to_ascii(s):# 如果输入是单个字符&#xff0c;直接返回其ASCII值if len(s) 1:return ord(s)# 否则返回每个字符的ASCII值列表return [ord(char) for char in s]def int_to_ascii(i):# 将整数转换为对应的ASCII字符return chr(i)if __…

气膜仓库出售:智能高效的新选择—轻空间

随着现代物流和仓储需求的不断增长&#xff0c;传统仓库模式已经难以满足市场的多样化需求。气膜仓库凭借其灵活性、高效性和智能化的特点&#xff0c;成为仓储领域的新宠&#xff0c;而现在&#xff0c;我们推出了全新方向——气膜仓库出售&#xff0c;为企业提供更加灵活的资…

hex 文件和 bin 文件剖析

目录 一、概述二、hex 文件三、bin 文件 在单片机开发中&#xff0c;hex 文件和 bin 文件是非常常见的两种烧写文件格式。比如在 Keil 中&#xff0c;编译好程序后&#xff0c;点击 Download 就可以把 hex 文件烧录到板子上。 而有时候在我们实现 IAP 时&#xff0c;有需要生成…

《动手学深度学习》笔记2.5——神经网络从基础→使用GPU (CUDA-单卡-多卡-张量操作)

目录 0. 前言 原书正文 1. 计算设备 (CPU和GPU) 补充&#xff1a;torch版本cuda报错的解决方案 2. 张量与GPU 3. 存储在GPU上 4. 复制&#xff08;多卡操作&#xff09; 5. 旁注 (CPU和GPU之间挪数据) 6. 神经网络与GPU 小结 0. 前言 课程全部代码&#xff08;pytorc…

Linux进程终止进程等待进程程序替换

目录 一、进程终止 1.1进程退出的场景 1.2进程常见的退出方法 ​1.3多进程的退出 ​1.4exit( )和_exit( ) 二、进程等待 2.1进程等待的必要性 2.2进程等待的方式 2.3获取子进程的statue 2.4非阻塞轮询 2.5进程等待的底层原理 三、进程程序替换 3.1单进程程序替换 …

踩坑Resilience4j @Bulkhead

先说问题情况 系统环境 java version: 17spring boot: 3.xspring cloud: 4.x 项目配置和代码 项目中使用了Spring Cloud Circuit Breaker <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-circuitbre…

Android 简单实现联系人列表+字母索引联动效果

效果如上图。 Main Ideas 左右两个列表左列表展示人员数据&#xff0c;含有姓氏首字母的 header item右列表是一个全由姓氏首字母组成的索引列表&#xff0c;点击某个item&#xff0c;展示一个气泡组件(它会自动延时关闭)&#xff0c; 左列表滚动并显示与点击的索引列表item …

Solidity智能合约调用其他合约的三种主要方式

在 Solidity 中&#xff0c;智能合约之间的交互非常重要。调用其他合约的功能可以增强合约的灵活性&#xff0c;使其能够执行跨合约操作&#xff0c;比如获取数据、转移资金或触发其他合约的功能。本文将详细介绍 Solidity 中调用其他合约的不同方式及其应用场景。 1. 合约间调…

【Unity踩坑】Unity更新Google Play结算库

一、问题描述&#xff1a; 在Google Play上提交了app bundle后&#xff0c;提示如下错误。 我使用的是Unity 2022.01.20f1&#xff0c;看来用的Play结算库版本是4.0 查了一下文档&#xff0c;Google Play结算库的维护周期是两年。现在需要更新到至少6.0。 二、更新过程 1. 下…

JAVA内存模型!=JVM内存模型

文章目录 前言JVM内存模型JAVA内存模型JAVA内存模型解释的问题可见性问题一致性问题 总结 前言 有很多JAVA开发人员&#xff0c;在被问起&#xff1a;“你知道Java内存模型吗&#xff1f;”&#xff0c;都会回答&#xff1a;“知道&#xff0c;JAVA内存模型分为方法区、堆、……

悟透自己、悟透生活、悟透人生(此文无价)

很多人都会有这样的疑问&#xff1a;“为什么听了很多道理&#xff0c;却依然没有过好这一生&#xff1f;” 古人给出了这样的回答。 王阳明曾说&#xff1a;“知行合一。” 老子则言&#xff1a;“知人者智&#xff0c;自知者明。” 可见&#xff0c;一切问题的根源都出在了我…

CSDN 的 GIt 是没东西吗

虽然说吧 CSDN 的博客也就那样&#xff0c;记得去年的时候 CSDN 出了一个 Git 代码库&#xff0c;被骂得要死&#xff0c;基本上是从外面搬了一堆代码回来。 这回 CSDN 又玩了个新东西&#xff0c;干脆你可以把你的博客文章同步到你在 CSDN 开的代码库上了。 如何同步 在 CS…

数据增强之imgaug的使用

包的导入 path = r"D:\\" # sometimes = lambda aug: iaa.Sometimes(0.5, aug) img = cv2.imread("D:\\photo\\test.jpg") img = cv2.resize(img,(128,128)) # img = cv2.cvtColor(img,cv2.COLOR_RGBA2GRAY) cv2.imwrite(path+"img.jpg",img)随…

python并发编程实战

python并发编程有三种 多线程Thread多进程Process多协程Coroutine cpu密集型计算 cpu密集型也叫计算密集型&#xff0c;是指I/O在很短的时间就可以完成&#xff0c;cpu需要大量的计算处理&#xff0c;特点是cpu占用率相当高 例如&#xff1a;压缩解压缩、加密解密、正则表达…

【Qt】开发环境与下载

这里写目录标题 1 Qt的开发工具概述2 Qt的下载2.1 下载Qt SDK 3. 认识SDK中的重要工具 1 Qt的开发工具概述 Qt支持持多种开发工具&#xff0c;其中⽐较常⽤的开发工具有&#xff1a;Qt Creator、Visual Studio、Eclipse. (1) QtCreator Qt Creator 是⼀个轻量级的跨平台集成…

iot网关是什么?iot网关在工业领域的应用-天拓四方

一、IoT网关的定义 IoT网关&#xff0c;即物联网网关&#xff0c;是物联网&#xff08;IoT&#xff09;系统中的重要组成部分。它主要实现感知网络与通信网络&#xff0c;以及不同类型感知网络之间的协议转换&#xff0c;既能够支持广域互联&#xff0c;也能满足局域互联的需求…

windows系统下Telnet工具的安装步骤

通过控制面板启用Telnet客户端 点击“确定”按钮&#xff0c;按照系统提示完成安装。 打开cmd&#xff0c;输入telnet就可以了