go学习之反射知识

反射

文章目录

    • 反射
      • 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)
 }

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

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

相关文章

设计模式-享元模式

设计模式专栏 模式介绍模式特点应用场景工厂模式和享元模式的区别代码示例Java实现享元模式python实现享元模式 享元模式在spring中的应用 模式介绍 享元模式是一种软件设计模式&#xff0c;它使用共享对象来减少内存使用量&#xff0c;并分享信息给尽可能多的相似对象。这种模…

【C语言程序设计】循环结构程序设计

目录 前言 一、程序设计第一题 二、程序设计第二题 三、程序设计第三题 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da…

C语言—每日选择题—Day45

第一题 1. 以下选项中&#xff0c;对基本类型相同的指针变量不能进行运算的运算符是&#xff08;&#xff09; A&#xff1a; B&#xff1a;- C&#xff1a; D&#xff1a; 答案及解析 A A&#xff1a;错误&#xff0c;指针不可以相加&#xff0c;因为指针相加可能发生越界&…

Geotrust中的dv ssl证书

DV SSL数字证书是入门级的数字证书&#xff0c;Geotrust的子品牌RapidSSL旗下的SSL数字证书产品都是入门级的SSL数字证书——DV基础型单域名SSL证书和DV基础型通配符SSL证书。今天就随SSL盾小编了解Geotrust旗下的DV SSL证书。 1.Geotrust旗下的DV基础型单域名SSL证书能够保护…

SAP ABAP excel文件模板上传下载

一&#xff1a;事物码smw0 二&#xff1a;上传步骤 程序源码&#xff1a; l_filename XX.xls.l_muban z123. *&---下载模板PERFORM frm_get_fullpath CHANGING gv_fullpath gv_path gv_name. *&---路径为空则退出IF gv_fullpath IS INITIAL.MESSAGE 用户取消操作 T…

DS查找——折半查找求平方根

Description 假定输入y是整数&#xff0c;我们用折半查找来找这个平方根。在从0到y之间必定有一个取值是y的平方根&#xff0c;如果我们查找的数x比y的平方根小&#xff0c;则x^2<y&#xff0c;如果我们查找的数x比y的平方根大&#xff0c;则x^2>y&#xff0c;我们可以据…

看图识药,python开发实现基于VisionTransformer的119种中草药图像识别系统

中药药材图像识别相关的实践在前面的系列博文中已经有了相应的实践了&#xff0c;感兴趣的话可以自行移步阅读即可&#xff0c;每篇文章的侧重点不同&#xff1a; 《python基于轻量级GhostNet模型开发构建23种常见中草药图像识别系统》 《基于轻量级MnasNet模型开发构建40种常…

Day58力扣打卡

打卡记录 下一个更大元素 IV&#xff08;单调栈 x2&#xff09; 链接 class Solution:def secondGreaterElement(self, nums: List[int]) -> List[int]:ans [-1] * len(nums)s []t []for i, x in enumerate(nums):while t and nums[t[-1]] < x:ans[t.pop()] x # t…

CSS的三大特性(层叠性、继承性、优先级---------很重要)

CSS 有三个非常重要的三个特性&#xff1a;层叠性、继承性、优先级。 层叠性 场景&#xff1a;相同选择器给设置相同的样式&#xff0c;此时一个样式就会覆盖&#xff08;层叠&#xff09;另一个冲突的样式。层叠性主要解决样式冲突 的问题 原则&#xff1a;  样式冲突&am…

nodejs微信小程序+python+PHP的外卖数据分析-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

Ubuntu编译文件安装SNMP服务

net-snmp源码下载 http://www.net-snmp.org/download.html 编译步骤 指定参数编译 ./configure --prefix/root/snmpd --with-default-snmp-version"2" --with-logfile"/var/log/snmpd.log" --with-persistent-directory"/var/net-snmp" --wi…

应用程序映射的 5 个安全优势

现代企业依靠无数的软件应用程序来执行日常运营。这些应用程序相互连接并协同工作以提供所需的服务。了解这些应用程序如何相互交互以及底层基础设施对于任何组织都至关重要。这就是应用程序映射概念的用武之地。 顾名思义&#xff0c;应用程序映射是创建应用程序架构&#xf…

[NAND Flash 3.2] 3D NAND 工艺与发展前沿

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 全文 6200 字&#xff0c;​2023.12.12 更新 1. 导论 1.1 何为 3D NAND? 3D NAND, 也叫做 Sumsung V-NAND, 是一种高密度闪存。 以前&#xff0c;把NAND闪存颗粒&#xff0c;直接…

互斥锁的原理

互斥锁&#xff08;Mutex&#xff0c;全称Mutual Exclusion&#xff09;是一种同步机制&#xff0c;用于确保在任意时刻&#xff0c;只有一个线程可以访问共享资源&#xff0c;从而防止数据竞争和不一致性。互斥锁的基本思想是在进入临界区之前&#xff0c;先获取锁&#xff1b…

Python和Beautiful Soup爬虫助力提取文本内容

大家好&#xff0c;网络爬虫是一项非常抢手的技能&#xff0c;收集、分析和清洗数据是数据科学项目中最重要的部分。今天介绍如何从链接中爬取高质量文本内容&#xff0c;我们使用迭代&#xff0c;从大约700个链接中进行网络爬取。如果想直接跳转到代码部分&#xff0c;可以在下…

【JVM从入门到实战】(四)类的生命周期

什么是类的生命周期 类的生命周期描述了一个类加载、连接、初始化、使用、卸载的整个过程 一个类完整的生命周期如下&#xff1a; 加载阶段 加载阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。 程序员可以使用Java代码拓展的不同的渠道…

计算机视觉 基于视频识别场景了解GluonCV深度学习工具包

一、简述 GluonCV 提供计算机视觉领域最先进 (SOTA) 深度学习算法的实现。它旨在帮助工程师、研究人员和学生快速制作产品原型、验证新想法并学习计算机视觉。 GluonCV: a Deep Learning Toolkit for Computer Vision — gluoncv 0.11.0 documentationhttps://cv.gluon.ai/con…

FFmpeg的AVIOPROBE

文章目录 定义 可能你一直有疑问&#xff0c;ffmpeg的avformat是怎么提前知道码流是编码格式或者容器&#xff1f;恭喜你&#xff0c;看到这里&#xff0c;你找到答案了&#xff0c;在这里&#xff0c;ffmpeg通过这些probe函数来提前获取码流的编码格式。 看到下面的avs2_prob…

单变量线性回归的机器学习代码

本文为学习吴恩达版本机器学习教程的代码整理&#xff0c;使用的数据集为https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes/blob/f2757f85b99a2b800f4c2e3e9ea967d9e17dfbd8/code/ex1-linear%20regression/ex1data1.txt 将数据集和py代码放到同一目录中&#xff0c;使…

多阶段构建:精妙优化Docker镜像大小和性能

在容器化应用的世界中&#xff0c;Docker镜像大小和性能优化是至关重要的。多阶段构建是一项强大的技术&#xff0c;通过精心设计Dockerfile&#xff0c;可以在构建镜像时去除不必要的组件&#xff0c;从而显著减小镜像大小&#xff0c;提高性能。本文章将深入讨论多阶段构建的…