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 mainimport ("fmt""reflect")//专门反射演示func reflectTest01(b interface{}) {//通过反射获取的传入的变量 type,kind,值//1.先获取到reflect.TyperTyp :=reflect.TypeOf(b)fmt.Println("rTyp=",rTyp) //int//2.获取到reflectValuerVal :=reflect.ValueOf(b) //100// n1 := 10// n2 := 2 + rVal// fmt.Println("n2=",n2)//erro // n1 := 10n2 := 2 + rVal.Int()fmt.Println("n2=",n2) //102fmt.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、先定义一个intvar num int = 100reflectTest01(num)}
-2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
 //专门演示对结构体的反射func reflectTest02(b interface{}) {//通过反射获取的传入的变量 type,kind,值//1.先获取到reflect.TyperTyp :=reflect.TypeOf(b)fmt.Println("rTyp=",rTyp) //rTyp= main.Student//2.获取到reflectValuerVal :=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 stringAge 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 intnum = 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 = 1b = 2
    )
    }
    

    2》还有一种专业的写法

    func main(){const(a = iotabc)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.ValuerVal := reflect.ValueOf(b)rVal.Elem().SetInt(20)
}
func main() {var num int = 10reflect01(&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 mainimport ("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.9reflect01(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 = names.Age = ages.Score = scores.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.Valueparams = append(params, reflect.ValueOf(10))params = append(params, reflect.ValueOf(40))res := val.Method(0).Call(params) //传入的参数是[]reflect.Value,返回[]reflect.Valuefmt.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 stringName string}func TestReflectStruct(t *testing.T){var {model *usersv 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.hqwc.cn/news/269574.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

锂电3V升12V1A升压芯片WT3209

锂电3V升12V1A升压芯片WT3209 WT3209是一款高功率密度全集成BOOST升压转换器&#xff0c;具备高效能解决方案。3V升12V1A,5V升12V1A WT3209内部集成的功率MOSFET管导通电阻为上管13mΩ和下管11mΩ&#xff0c;具备2A开关电流能力&#xff0c;并且能够提供高达12.6V的输出电压。…

Java - Math类的常用方法及练习

目录 1.1 概述 1.2 常用方法 ❓面试题&#xff1a;为啥Math.round(-1.5)-1? 1.1 概述 java.lang.Math 类包含用于执行基本数学运算的方法&#xff0c;如初等指数、对数、平方根和三角函数。类似这样的工具类&#xff0c;其所有方法均为静态方法&#xff0c;并且不会创建对象…

基于PaddleNLP的深度学习对文本自动添加标点符号(一)

前言 目前以深度学习对文本自动添加标点符号研究很少&#xff0c;已知的开源项目并不多&#xff0c;详细的介绍就更少了&#xff0c;但对文本自动添加标点符号又在古文识别语音识别上有重大应用。 基于此&#xff0c;本文开始讲解基于PaddleNLP的深度学习对文本自动添加标点符号…

论文阅读——ScanQA

ScanQA: 3D Question Answering for Spatial Scene Understanding 输入&#xff1a;点云P和问题Q&#xff0c;输出&#xff1a;答案A 点云p由三维坐标点组成。本文模型使用额外的点云特征&#xff1a;点云高度、颜色、法线和多视图图像特征&#xff0c;这些特征将 2D 外观特征投…

链游成为蓝海,潮游世界开创未来新时代

区块链、元宇宙浪潮来袭&#xff0c;为数字世界开启崭新的大门&#xff0c;一场链游模式的范式革命正在发生&#xff01; 未来&#xff0c;元宇宙中&#xff0c;链游将成为中坚力量。 潮游世界抢占时代先机&#xff0c;利用区块链技术的去中心化和数字资产的不可替代性&#x…

PyQt6 QToolBar工具栏控件

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计44条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

基于Qt的蓝牙Bluetooth在ubuntu实现模拟

​# 前言 Qt 官方提供了蓝牙的相关类和 API 函数,也提供了相关的例程给我们参考。笔者根据 Qt官方的例程编写出适合我们 Ubuntu 和 gec6818开发板的例程。注意 Windows 上不能使用 Qt 的蓝牙例程,因为底层需要有 BlueZ协议栈,而 Windows 没有。Windows 可能需要去移植。笔者…

Linux shell编程学习笔记35:seq

0 前言 在使用 for 循环语句时&#xff0c;我们经常使用到序列。比如&#xff1a; for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i * 2 $(expr $i \* 2)"; done 其中的 1 2 3 4 5 6 7 8 9 10;就是一个整数序列 。 为了方便我们使用数字序列&#xff0c;Linux提供了…

【Netty的线程模型】

Netty的线程模型 Netty的线程模型知识拓展单Reactor单线程模型单Reactor多线程模型主从Reactor模型 Netty的线程模型 Netty通过Reactor模型基于多路复用器接收并处理用户请求的&#xff0c;多路复用IO模型参考&#xff1a; 多路复用IO模型: 操作系统的IO模型有哪些&#xff1f…

IT圈茶余饭后的“鄙视链” C,C++,Java,Python

目录 C语言的自尊心 C语言的历史与地位 C语言的支持者心态 鄙视链的表现 自尊心的盲点 C的复杂之美 多范式编程的复杂性 高度的控制权 模板元编程的奇妙 面向对象的强大 Java的跨平台与舒适感 跨平台性的优势 舒适的开发体验 对其他语言的轻蔑 面向企业级应用的自…

HTML中常用表单元素使用(详解!)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍HTML中常用表单元素使用以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留言 …

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;…