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

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

本节也是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.kler.cn/a/325835.html

相关文章:

  • 机器学习基础04
  • Android 使用Retrofit 以纯二进制文件流上传文件
  • 数据科学与SQL:如何计算排列熵?| 基于SQL实现
  • vue项目使用eslint+prettier管理项目格式化
  • nuget 管理全局包、缓存和临时文件夹
  • 如何在 Ubuntu 上安装 Emby 媒体服务器
  • C++ | Leetcode C++题解之第447题回旋镖的数量
  • 汽车EDI:Martinrea EDI 对接
  • 自动驾驶系统研发系列—智能驾驶守门员:详解DOW(开门预警)功能,开启更安全的驾驶体验
  • 字节C++抖音直播一面-面经总结
  • JAVA线程基础二——锁的概述之乐观锁与悲观锁
  • 【前端】ES12:ES12新特性
  • 【Python报错已解决】TypeError: ‘list‘ object is not callable
  • 探索AI新纪元:揭秘Mammoth库的神秘面纱
  • plt.bar函数介绍及实战
  • linux服务器部署filebeat
  • 【30天玩转python】自动化与脚本编写
  • 各种 JIT(Just-In-Time) 编译器
  • Python | Leetcode Python题解之第446题等差数列划分II-子序列
  • 鸿蒙harmonyos next flutter通信之MethodChannel获取设备信息
  • 【Python】Curdling:Python 包管理的高效工具
  • 基于STM32与OpenCV的物料搬运机械臂设计流程
  • 卷积神经网络 循环神经网络
  • 定时任务上云改造方案
  • 【MySQL】常见的SQL优化方式(二)
  • 设计模式 命令模式(Command Pattern)