go学习之反射知识

news2024/12/23 5:30:18

反射

文章目录

    • 反射
      • 1、反射的使用场景
        • 1)结构体标签的应用
        • 2)使用反射机制编写函数的适配器(桥连接)
      • 2、反射的基本介绍
        • -1.基本介绍
        • -2.反射的图解
        • -3.反射重要的函数和概念
      • 3.反射快速入门
        • -1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
        • -2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
      • 4.反射的注意事项和细节说明
        • 1)reflect.Value.Kind,获取变量的类别,返回的是一个常量
        • 2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
      • 5.反射练习题
      • 6.反射的最佳实践

1、反射的使用场景

1)结构体标签的应用

在这里插入图片描述

2)使用反射机制编写函数的适配器(桥连接)

在这里插入图片描述

2、反射的基本介绍

-1.基本介绍

1)反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)

2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

3)通过反射,可以修改变量的值,可以调用关联的方法

4)使用反射,需要import (“reflect”)

-2.反射的图解

在这里插入图片描述

-3.反射重要的函数和概念

1)reflect.TypeOf(变量名)。获取变量的类型,返回reflect.Type类型

2)reflect.ValueOf(变量名)。获取变量的值,返回reflect.Value类型reflect.Value是一个结构体类型。通过reflect.Value.可以获取到关于该变量的很多信息

3)变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。

3.反射快速入门

-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。

代码演示

package main
 import (
	"fmt"
	"reflect"
 )

 //专门反射演示
 func reflectTest01(b interface{}) {
	//通过反射获取的传入的变量 type,kind,值
	//1.先获取到reflect.Type
	rTyp :=reflect.TypeOf(b)
	fmt.Println("rTyp=",rTyp) //int
	//2.获取到reflectValue
	rVal :=reflect.ValueOf(b) //100
	// n1 := 10
	// n2 := 2 + rVal
	// fmt.Println("n2=",n2)//erro 

	// n1 := 10
	n2 := 2 + rVal.Int()
	fmt.Println("n2=",n2) //102

	fmt.Printf("rVal=%v type=%T\n",rVal,rVal) //rVal=100 type=reflect.Value

	//下面我们将rVal转成interface{}
	iv := rVal.Interface()
	//将interface{}通过断言转成需要的类型
	num2 := iv.(int)
	fmt.Println("num2=",num2) //num2= 100
 }
 func main() {
	//-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
	
	//1、先定义一个int
	var num int = 100
	reflectTest01(num)
 }
-2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
 //专门演示对结构体的反射
 func reflectTest02(b interface{}) {
	//通过反射获取的传入的变量 type,kind,值
	//1.先获取到reflect.Type
	rTyp :=reflect.TypeOf(b)
	fmt.Println("rTyp=",rTyp) //rTyp= main.Student

	//2.获取到reflectValue
	rVal :=reflect.ValueOf(b)
	
	//下面我们将rVal转成interface{}
	iv := rVal.Interface()
	fmt.Printf("iv=%v ,iv type=%T\n",iv,iv) //iv={Tom 20} ,iv type=main.Student
	//将interface{}通过断言转成需要的类型
	// fmt.Printf("iv=%v ,iv type=%T name\n",iv,iv,iv.Name) //erro无法取出name的值
	//所以先要进行断言
	stu,ok :=iv.(Student)
	if ok {
		fmt.Printf("stu.Name=%v\n",stu.Name)//stu.Name=Tom
	}

	
 }

 type Student struct {
	Name string
	Age int
 }
 func main() {


	//2.定义一个Student的实例
	stu := Student{
		Name : "Tom",
		Age : 20,
	}

	reflectTest02(stu)

 }

4.反射的注意事项和细节说明

1)reflect.Value.Kind,获取变量的类别,返回的是一个常量

补充常量是知识

常量介绍

  • 常量使用const修改

  • 常量在定义的时候,必须初始化

  • 常量不能修改

  • 常量只能修饰bool、数值类型(int,float系列)、string类型

    语法: const identifier [type] = value

    package main
    import (
    	"fmt"
    )
    func main() {
    	var num int
    	num = 90 
    	//常量声明的时候必须赋值
    	const tax int = 90
    	// tax = 10 //常量是不能修改的
    	fmt.Println(num,tax)
        //常量只能修饰bool、数值类型(int,float系列)、string类型
        //const b =num /3 erro
    }
    

    常量使用注意事项

    1》比较简洁的写法

    func main () {
    const(
    	a = 1
    	b = 2
    )
    }
    

    2》还有一种专业的写法

    func main(){
    	const(
    	a = iota
        b
        c
    	)
    	fmt.Println(a,b,c)//0,1,2
    }
    

    3》Golang中没有常量名必须大写的规范

    4》仍然通过首字母的大小来控制常量的访问范围

2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的

比如: var num int = 10 num 的Type是int, Kind也是int

比如:var stu Student stu 的Type是包名.Student,Kind是struct(Kind的等级 >Type的等级)

3)通过反射可以让变量在interface{}和Reflect.Value之间相互转换

4)使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x)Int(),而不能使用其他的,否则报panic

5)通过反射的值来修改变量。注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的值,同时需要使用到reflect.Value.Elem()方法

package main
import (
	"fmt"
	"reflect"
)

//通过反射,修改
//num int 的值
//修改student的值

func reflect01(b interface{}){
	//获取到reflect.Value
	rVal := reflect.ValueOf(b)
	rVal.Elem().SetInt(20)
}
func main() {
	var num int = 10
	reflect01(&num)
	fmt.Println("num=",num) //20


	//你可以这样理解这句话:rVal.Elem()
	// num := 9
	//  ptr *int = &num
	//  num2 :=*ptr
}

6)如何理解rVal.Elem()

//你可以这样理解这句话:rVal.Elem()
	// num := 9
	//  ptr *int = &num
	//  num2 :=*ptr
}

5.反射练习题

1)给你一个变量 var v float64 = 1.2.请使用反射来得到它的reflect.Value,然后获取对应的Type,Kind和值,并将reflect.Value转换成interface{}.,再将interface{}转换成float64

package main
 import (
	"fmt"
	"reflect"
 )
 func reflect01(b interface{})  {
	num := reflect.ValueOf(b)
    kind1 :=num.Kind()
	iv :=num.Interface()
	fmt.Printf("b的reflect.Value是=%v,kind值为=%v,num转换为interface的值为=%v",num,kind1,iv)
 }
 func main() {
	var n float64 =65.9
	reflect01(n)
 }

2)给字符串改名题

        var str string = "tom"
        fs :=reflect.ValueOf(&str) //这里要改成地址
        fs.Elem().SetString("jackma")
        fmt.Println(str)

6.反射的最佳实践

1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
2)使用反射的方式来获取结构体的tag标签,遍历字段的值,修改字段值,调用结构体方法
val.Method(0).Call() 调用第一个方法
方法的排序默认是按照函数名的排序(ASCII码)
定义了两个函数testl 和test2, 定义一个适配器函数用作统一 处理接口
使用反射操作任意结构体类型
使用反射创建并操作结构体

package main
import (
	"fmt"
	"reflect"
)

//定义了一个Monster结构体
type Monster struct{
	Name string `json:"name"`
	Age int `json:"age"`
	Score float32 `json:"成绩"`
	Sex string
}

// 方法: 返回两数之和
func(s Monster) GetSum(n1, n2 int) int{
	return n1 + n2
}

// 方法: 接收四个值, 给S赋值
func(s Monster) Set(name string, age int, score float32, sex string){
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

//方法,显示s的值
func (s Monster) Print() {
	fmt.Println("--start--")
	fmt.Println(s)
	fmt.Println("--end--")
}

func TestStruct(a interface{}){
	//获取reflect.Type类型
	typ := reflect.TypeOf(a)
	//获取reflect.Value类型
	val := reflect.ValueOf(a)
	//获取到a对应的类别
	kd := val.Kind()
	//如果传入的不是struct,就退出
	if kd != reflect.Struct {
		fmt.Println("Expect struct")
		return
	}
	//获取到该结构体有几个字段
	num := val.NumField()
	fmt.Printf("struct has %d fields\n", num) //4
	//变量结构体的所有字段
	for i := 0; i < num; i++ {
		fmt.Printf("Field %d:值为=%v\n", i, val.Field(i))
		//获取到struct 标签,注意需要通过reflect.Type来获取tag标签的值
		tagVal := typ.Field(i).Tag.Get("json")
		//如果该字段于tag标签就显示,否则就不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
		}
	}
	//获取到该结构体有多少个方法
	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d methods\n", numOfMethod)
	//var params []reflect.Value
	//方法的排序默认是按照函数名的排序(ASCII码)
	val.Method(1).Call(nil)//获取到第二个方法。调用它
	
	//调用结构体的第1个方法Method(0)
	var params []reflect.Value //声明了[]reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params) //传入的参数是[]reflect.Value,返回[]reflect.Value
	fmt.Println("res=", res[0].Int()) //返回结果,返回的结果是[]reflect.Value*/
}

func main(){
	var a Monster = Monster{
		Name: "黄鼠狼精",
		Age: 400,
		Score: 30.8,
	} 
	//将Monster实例传递给TestStruct函数
	TestStruct(a)
}

2)使用反射操作任意结构体类型

type user struct {
	UserId string
	Name string
 }

 func TestReflectStruct(t *testing.T){
	var {
		model *user
		sv reflect.Value
	}
	model = &user{}
	sv = reflect.ValueOf(model)
	t.Log("reflect.ValueOf",sv.kind().String())
	sv=sv.Elem()
	t.Log("reflect.ValueOf",,sv.kind().String())
	sv.FieldByName("userId").SetString("12345678")
	sv.FieldByName("Name").SetString("nickname")
	t.Log("model",model)
 }

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

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

相关文章

【Vue】router.push用法实现路由跳转

目录 router.push用法 在Login.vue中 在Register.vue中 ​ 上一篇&#xff1a;登录与注册界面的制作 https://blog.csdn.net/m0_67930426/article/details/134895214?spm1001.2014.3001.5502 制作了登录与注册界面&#xff0c;并介绍了相关表单元素即属性的用法 在登录页面…

OpenHarmony应用开发——创建第一个OpenHarmonry工程

一、前言 本文主要介绍DevEco Studio的相关配置&#xff0c;以及创建第一个OpenHarmony应用程序。 二、详细步骤 打开DevEco Studio. 进入Settings. 随后SDK选择OpenHarmony&#xff0c;并完成下述API的选择与下载. 等待下载完成后&#xff0c;创建第一个Project. 此处选择Emp…

区块链技术的未来,了解去中心化应用的新视角

小编介绍&#xff1a;10年专注商业模式设计及软件开发&#xff0c;擅长企业生态商业模式&#xff0c;商业零售会员增长裂变模式策划、商业闭环模式设计及方案落地&#xff1b;扶持10余个电商平台做到营收过千万&#xff0c;数百个平台达到百万会员&#xff0c;欢迎咨询。 随着…

C/C++,动态 DP 问题的计算方法与源程序

1 文本格式 #include <bits/stdc.h> using namespace std; typedef long long LL; const int maxn 500010; const int INF 0x3f3f3f3f; int Begin[maxn], Next[maxn], To[maxn], e, n, m; int size[maxn], son[maxn], top[maxn], fa[maxn], dis[maxn], p[maxn], i…

推荐开源项目-网络应用协议框架Socket.D

基于事件和语义消息流的网络应用协议 Socket.D 0 代码仓库地址1 该开源项目特点2 项目结构3 核心理念-协议帧Frame4 结束语 0 代码仓库地址 https://gitee.com/noear/socketd 1 该开源项目特点 代码风格优雅文档说明齐全测试用例非常人性化上手快&#xff0c;代码用例很多代…

基于双树复小波变换和稀疏表示的多光谱和彩色图像融合算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 双树复小波变换原理 4.2 稀疏表示原理 4.3 基于双树复小波变换和稀疏表示的图像融合算法 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序…

Appium 自动化自学篇 —— 初识Appium自动化!

Appium 简介 随着移动终端的普及&#xff0c;手机应用越来越多&#xff0c;也越来越重要。而作为测试 的我们也要与时俱进&#xff0c;努力学习手机 App 的相关测试&#xff0c;文章将介绍手机自动化测试框架 Appium 。 那究竟什么是 Appium 呢? 接下来我们一起来学习PythonS…

Spring 的设计思想、创建和使用、Bean 作用域和生命周期

文章目录 Spring 设计思想Spring 是什么&#xff1f;什么是 IoC&#xff1f; Spring 创建和使用创建 Spring 项目注册 Bean 对象获取并使用 Bean 对象 Spring 更方便地存储和读取对象配置文件使用注解使用类注解使用方法注解 获取 Bean 对象属性注入Setter 注入构造方法注入Res…

基于FPGA的视频接口之高速IO(光纤)

简介 对于高速IO口配置光纤,现在目前大部分开发板都有配置,且也有说明,在此根据自己的工作经验以及对于各开发板的说明归纳 通过高速IO接口,以及硬件配置,可以实现对于光纤的收发功能,由于GTX的速率在500Mbs到10Gbps之间,但通道高速io可配置光纤10G硬件,物理通完成,则…

Unity 修改游戏对象的旋转角度Rotation的方法

在Unity中要修改游戏对象中的旋转角度&#xff0c;即下图中的Rotation: 有三个方法&#xff1a; 1、 使用欧拉角&#xff08;Euler Angles&#xff09;&#xff1a;欧拉角是一组表示旋转的三个角度值&#xff08;绕X轴的旋转、绕Y轴的旋转和绕Z轴的旋转&#xff09;。 transf…

Unity光照模型实践

光照作为3D渲染中最重要的部分之一&#xff0c;如何去模拟真实环境的光照是重要的研究内容&#xff0c;但是现实环境光照过于复杂&#xff0c;有很多经典好用的光照模型去近似真实光照。 根据基础的Phong模型 最终某个点的结果为 环境光Ambient 漫反射光Diffuse 高光Specula…

Hadoop高可用(主备切换)---配合Zookeeper

1. Hadoop高可用(Hadoop High Availability)概述 HA(High Available), 高可用&#xff0c;是保证业务连续性的有效解决方案&#xff0c;一般有两个或两个以上的节点&#xff0c;分为活动节点&#xff08;Active&#xff09;及备用节点&#xff08;Standby&#xff09;。通常把…

Mendix 创客访谈录|合作伙伴谈Mendix对销售团队的业务价值

本期创客 段晓科 适途科技 咨询中心负责人 大家好&#xff0c;我叫段晓科&#xff0c;非软件开发专业&#xff0c;拥有多年制造行业数字化解决方案经验&#xff0c;擅长从0到1 的产品构建和产品落地服务。 目前任职于适途科技&#xff0c;自2021年3月至今负责Mendix低代码平台…

sonar扫描代码用法

1 mvn的setting配置文件中配置如下 其中sonar.host.url是本地或者服务器上自己安装的sonar <pluginGroups><pluginGroup>org.sonarsource.scanner.maven</pluginGroup></pluginGroups><profile><id>sonar</id><activation>&l…

boost graph之基础

结构 属性相关 #mermaid-svg-PZQvKfbwNSnyXO9g {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-PZQvKfbwNSnyXO9g .error-icon{fill:#552222;}#mermaid-svg-PZQvKfbwNSnyXO9g .error-text{fill:#552222;stroke:#5522…

文心一言 VS 讯飞星火 VS chatgpt (155)-- 算法导论12.3 2题

二、用go语言&#xff0c;假设通过反复向一棵树中插人互不相同的关键字来构造一棵二叉搜索树。证明&#xff1a;在这棵树中查找关键字所检查过的结点数目等于先前插入这个关键字所检查的结点数目加 1。 文心一言&#xff1a; 为了证明这个结论&#xff0c;我们可以使用二叉搜…

【代码随想录】刷题笔记Day34

前言 考过概率论&#xff0c;发过一场烧&#xff0c;兜兜转转又一月&#xff0c;轻舟已撞万重山&#xff0c;赶紧刷题 贪心算法理论基础 贪心的本质&#xff1a;局部最优→全局最优无套路&#xff0c;常识性推导 举反例 455. 分发饼干 - 力扣&#xff08;LeetCode&#xf…

Python (六) 绘图

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

故障注入测试有哪些多重作用?

在软件开发的世界中&#xff0c;保证系统的鲁棒性和稳定性至关重要。为了应对各种潜在的故障和异常情况&#xff0c;测试团队采用了各种测试方法&#xff0c;其中之一就是故障注入测试。这种测试方法的目标是有目的地向系统引入故障&#xff0c;以评估系统在面对异常情况时的表…

在pom.xml中添加maven依赖,但是类里面import导入的时候报错

问题&#xff1a; Error:(27, 8) java: 类TestKuDo是公共的, 应在名为 TestKuDo.java 的文件中声明 Error:(7, 23) java: 程序包org.apache.kudu不存在 Error:(8, 23) java: 程序包org.apache.kudu不存在 Error:(9, 23) java: 程序包org.apache.kudu不存在 Error:(10, 30) jav…