Go反射学习

news2025/1/13 13:28:16

文章目录

  • 反射介绍:
  • 反射应用点
  • 变量-空接口-reflect.Value(Type)
  • 类型
  • 方法
  • 结构体:
  • 反射修改变量值
  • 反射操作结构体
    • Method
    • Call

反射介绍:

反射是在运行时,动态的获取变量的各种信息,如变量的类型,类别等信息

可以获得结构体的信息(字段,方法)

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

使用反射需要导入reflect包

反射弥补了静态语言上的不足

反射是实现元编程的重要手段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUFHADcy-1673444656027)(../images/Pasted%20image%2020230111203412.png)]

reflect.excalidraw

在反射中,变量,空接口,reflect.Value是可以相互转换的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3yWPwob-1673444656027)(../images/Pasted%20image%2020230111204248.png)]

反射应用点

结构体的tag

自己可以写go框架的时候,函数的适配器

变量-空接口-reflect.Value(Type)

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func reflectTest(b interface{}) {  
   //得到一个reflect.Type类型  
   rType := reflect.TypeOf(b)  
  
   fmt.Println(rType)  
  
   //得到一个reflect.Value类型  
   rVal := reflect.ValueOf(b)  
   fmt.Println(rVal)  
   fmt.Printf("%T\n", rVal)  
   //reflect.Value类型转换成空接口  
   iV := rVal.Interface()  
   var num2 = iV.(int)  
   fmt.Println(num2)  
  
}  
  
func main() {  
   var num int = 100  
   reflectTest(num)  
  
}

类型

一个简单的基本类型反射判断

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func main() {  
   var (  
      a = 100  
   )  
   t := reflect.TypeOf(a)  
   fmt.Println(t)  
  
}

输出
int
打印出了变量a的数据类型

对于自定义类型的类型反射判断

package main  
  
import (  
   "fmt"  
   "reflect")  
  
type sim int  
  
func main() {  
   var a sim = 100  
   t := reflect.TypeOf(a)  
   //type是判断类型(静态类型),kind是判断基础结构(底层类型)  
   fmt.Println(t.Name(), t.Kind())  
  
}

输出:
sim int

对于指针

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func main() {  
   a := 10  
   t1, t2 := reflect.TypeOf(a), reflect.TypeOf(&a)  
  
   fmt.Println(t1, t2)  
   fmt.Println(t1 == t2.Elem())  
}

输出:

int *int
true

方法Elem返回指针,数据,切片,字典或通道的基类型。
所以这里的判断是true
拓展:

func main()  {
    fmt.Println(reflect.TypeOf(map[string]int()).Elem())
    fmt.Println(reflect.TypeOf([]int32{}).Elem())
}

输出:
int
int32

得到的都是他们的基类型

对于结构体

package main  
  
import (  
   "fmt"  
   "reflect")  
  
type student struct {  
   name string  
   age  int  
}  
  
func main() {  
   var simple student  
  
   t := reflect.TypeOf(&simple)  
  
   //fmt.Println(t) 获取的结构体指针  
  
   //fmt.Println(reflect.Ptr)  
   //fmt.Println(t.Kind())   //判断是不是指针类型  
   if t.Kind() == reflect.Ptr {  
      t = t.Elem()  
      //fmt.Println(t)取得基类,是一个结构体main.student  
   }  
   //遍历结构体  
   for i := 0; i < t.NumField(); i++ {  
      f := t.Field(i)  
      fmt.Println(f.Name, f.Type, f.Offset)  
  
      if f.Anonymous {  
         for x := 0; x < f.Type.NumField(); x++ {  
            af := f.Type.Field(x)  
            fmt.Println(" ", af.Name, af.Type)  
         }  
      }  
   }  
}

输出:
name string 0
age int 16

取得变量的值
type获取类型,value可以获取值。

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func main() {  
   a := "simple"  
   t := reflect.ValueOf(a)  
   fmt.Println(t)  
}

方法

package main  
  
import (  
   "fmt"  
   "reflect")  
  
type X struct{}  
  
func (X) Test(x, y int) (int, error) {  
   return x + y, fmt.Errorf("err:%d", x+y)  
}  
  
func main() {  
   var a X  
  
   v := reflect.ValueOf(&a)  
   m := v.MethodByName("Test")  
  
   in := []reflect.Value{  
      reflect.ValueOf(1),  
      reflect.ValueOf(2),  
   }  
  
   out := m.Call(in)  
   for _, v := range out {  
      fmt.Println(v)  
   }  
}

3
err:3

结构体:

package main  
  
import (  
   "fmt"  
   "reflect")  
  
type Student struct {  
   name string  
   age  int  
}  
  
func reflectTest1(b interface{}) {  
   //得到一个reflect.Type类型  
   rType := reflect.TypeOf(b)  
  
   fmt.Println(rType)  
  
   //得到一个reflect.Value类型  
   rVal := reflect.ValueOf(b)  
   fmt.Println(rVal)  
   fmt.Printf("%T\n", rVal)  
  
   // reflect.Value类型变成空接口  
   iV := rVal.Interface()  
   fmt.Printf("%v,%T \n", iV, iV)  
   // 通过断言转换成需要的类型  
   stu, ok := iV.(Student)  
   if ok {  
      fmt.Println("stu.name:", stu.name)  
   }  
}  
  
func main() {  
  
   stu := Student{  
      "simple",  
      10,  
   }  
   reflectTest1(stu)  
}

反射修改变量值

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func reflect1(b interface{}) {  
   rVal := reflect.ValueOf(b)  
   //fmt.Println(rVal.Kind()) 类别是一个指针  
   rVal1 := rVal.Elem()//取指针指向的值  
   rVal1.SetInt(20)  
}  
  
func main() {  
   num := 100  
   reflect1(&num)  
   fmt.Println(num)  
}

输出
20

package main  
  
import (  
   "fmt"  
   "reflect")  
  
func main() {  
   name := "simple"  
   rVal := reflect.ValueOf(&name)  
   rVal.Elem().SetString("jack")  
   fmt.Println(name)  
}

输出:
jack

反射操作结构体

Method

func (Value) [Method]

func (v [Value]) Method(i int) Value

返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果i出界,或者v的持有值是接口类型的零值(nil),会panic。

Call

func (Value) Call

func (v Value) Call(in [][Value]) [][Value]

Call方法使用输入的参数in调用v持有的函数。例如,如果len(in) == 3,v.Call(in)代表调用v(in[0], in[1], in[2])(其中Value值表示其持有值)。如果v的Kind不是Func会panic。它返回函数所有输出结果的Value封装的切片。和go代码一样,每一个输入实参的持有值都必须可以直接赋值给函数对应输入参数的类型。如果v持有值是可变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面。

package main  
  
import (  
   "fmt"  
   "reflect")  
  
// 定义结构体  
type Monster struct {  
   Name  string `json:"name"`  
   age   int    `json:"age"`  
   Score float32  
   sex   string  
}  
  
// Print方法  
func (s Monster) Print() {  
   fmt.Println("start")  
   fmt.Println(s)  
   fmt.Println("end")  
  
}  
  
// 和方法  
func (s Monster) GetSum(n1, n2 int) int {  
   return n1 + n2  
}  
  
// Set方法  
func (s Monster) Set(name string, age int, score float32, sex string) {  
   s.Name = name  
   s.age = age  
   s.Score = score  
   s.sex = sex  
}  
  
func testStruct(a interface{}) {  
   //获取reflect.Type和reflect.Value  
   typ := reflect.TypeOf(a)  
   val := reflect.ValueOf(a)  
   //看看类别  
   kd := val.Kind()  
   //看看是不是结构体  
   if kd != reflect.Struct {  
      fmt.Println("不是结构体")  
      return  
   }  
   //获取字段数量  
   num := val.NumField()  
   fmt.Printf("struct has %d fields\n", num)  
   //遍历字段  
   for i := 0; i < num; i++ {  
      fmt.Printf("Field %d : 值为:%v\n", i, val.Field(i))  
      //获取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("结构体有%d个方法\n", numofMethod)  
   //调用第二个函数,第二个函数没有参数  
   val.Method(1).Call(nil)  
   //反射的函数排序是按照函数名的ascii码来排序的,G,P,S来排序的,所以这里的第二个就是Print  
  
   //准备函数的参数,参数要是[]reflect.Value切片类型的  
   var params []reflect.Value  
   params = append(params, reflect.ValueOf(10))  
   params = append(params, reflect.ValueOf(40))  
   //一个10,一个是40  
   //调用函数,并传入参数  
   res := val.Method(0).Call(params)  
   //res还是一个切片,返回的是一个切片  
   fmt.Println(res)  
  
   fmt.Println("res= ", res[0].Int())  
  
}  
  
func main() {  
   //定义一个实例  
   a := Monster{  
      "simple",  
      20,  
      30.1,  
      "难",  
   }  
   //进入反射  
   testStruct(a)  
  
}

输出:

struct has 4 fields
Field 0 : 值为:simple
Field 0: tag为:name  
Field 1 : 值为:20    
Field 1: tag为:age   
Field 2 : 值为:30.1  
Field 3 : 值为:难    
结构体有3个方法       
start                 
{simple 20 30.1 难}
end
50
res=  50

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

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

相关文章

Springboot中如何优雅的写好Service层代码

前言《Springboot中如何优雅的写好controller层代码》一不小心进入了全站综合热榜&#xff0c;收到了大家热情的支持&#xff0c;非常感谢大家&#xff0c;同时说明大家都有同样一个诉求&#xff0c;想好好写代码&#xff0c;不想给别人挖坑&#xff0c;争取可以早点下班。今天…

【Spring源码】CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

CommonAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()中一共包含3个方法&#xff0c;上篇文章我们介绍了的第一个方法&#xff0c;它一个父类调用&#xff08;如下图&#xff09;&#xff0c;其实就是处理PostConstruct和PreDestroy这两个注解的这篇我们继续…

一起聊聊数据治理

统一赵秦车轨&#xff0c;推行秦篆&#xff0c;统一七国文字&#xff0c;兵器统一标准&#xff0c;统一度量衡… 我们优秀的数据治理专家-秦始皇&#xff01; 数据治理这个名字起得好&#xff0c;一般人听不懂&#xff0c;实际上并不是IT人员的专属&#xff0c;广义上来说我们日…

纳米软件分享:为什么要进行电池充放电测试?电池充放电系统测试步骤

在日常生活中电能一直是我们接触过的最为方便的能源&#xff0c;而我们也通过各种方法对电能进行储存从而让我们能随时随地的使用这种能源&#xff0c;比如手机中的锂电池、电动车中的充电电池等。 充电锂电池经过多年的技术革新&#xff0c;综合性能不断提升&#xff0c;已经应…

基于node.js和Vue的食堂窗口美食评价系统/美食网站

摘要本论文主要论述了如何使用Node.js语言开发一个食堂窗口美食评价系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述食堂窗口美食评价系统的当前背景以及系…

【高级人工智能】国科大《高级人工智能》联结主义 笔记 + 考试回忆

国科大《高级人工智能》吴老师部分——联结主义笔记 吴老师上课dddd&#xff0c;上课东西太多太杂&#xff0c;听不太懂比较煎熬&#xff0c;但是课后花点时间理解理解&#xff0c;还是挺有帮助的考试按照重点复习即可&#xff0c;虽然答疑时提到的传教士野人没考&#x1f605;…

神经网络、激活函数

目录1.双层神经网络计算神经网络层数的时候不包括输入层。2.逻辑回归的神经网络如何实现隐藏单元如何计算&#xff1f;&#xff0c;3x1矩阵&#xff0c;3x1矩阵,上标[1]表示第一层向量化(单个训练样本):隐藏层&#xff1a;&#xff0c;&#xff0c;为4x3矩阵&#xff0c;x为3x1…

spring cloud gateway 整合sentinel使用过程使用遇到的问题

最近在进行spring cloud gateway 整合 sentinel 在此过程中遇到的问题进行汇总 1. spring gateway 整合sentinel gateway的路由会自动加一个前缀 效果如下 问题原因 代码在 org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator#DiscoveryClie…

【迅为iMX6Q】开发板 Linux 5.15.71 RTL8211E 以太网驱动适配

相关参考 【迅为iMX6Q】开发板 u-boot 2022.04 SD卡 启动 【迅为iMX6Q】开发板 u-boot 2020.04 RTL8211E 以太网驱动适配 【迅为iMX6Q】开发板 Linux 5.15.71 SD卡 启动 开发环境 win10 64位 VMware Workstation Pro 16 ubuntu 22.04 -【迅为imx6q】开发板&#xff0c;…

【学习笔记】【Pytorch】三、常用的Transforms

【学习笔记】【Pytorch】三、常用的Transforms学习地址主要内容一、Transforms模块介绍二、transforms.ToTensor类的使用1.使用说明2.代码实现三、transforms.Normalize类的使用1.使用说明2.代码实现四、transforms.Resize类的使用1.使用说明2.代码实现五、transforms.Compose类…

IIC协议初识及OLED写入指令、数据和显示的代码实现

目录 一、IIC协议概述 二、IIC协议起始和终止信号 三、应答信号 四、数据发送的时序 五、OLED写入指令和数据 六、OLED显示一个点的思路 七、OLED显示一个点的代码 八、OLED列地址 九、OLED显示图片 一、IIC协议概述 1、IIC概述 IIC&#xff08;集成电路总线&#…

UI设计师与UE设计师有什么区别?

设计在我们的生活中扮演着重要的角色&#xff0c;但是面对越来越多的专业术语和相近的岗位职责&#xff0c;人们总是困惑&#xff1a;明明是差不多的岗位&#xff0c;为什么要整那么多的词&#xff1f;其实&#xff0c;在它们神似的外表下&#xff0c;潜藏着巨大的差异&#xf…

虹科分享 | 实时操作系统INtime RTOS第7版功能更新介绍

INtime简介 INtime是专为基于PC的嵌入式解决方案而设计的可扩展实时操作系统&#xff0c;功能包括动态控制多个主机上多个节点&#xff08;核心&#xff09;上的进程&#xff0c;以及系统所有节点之间的丰富进程间通信&#xff0c;可应用于多核x86兼容处理器的非对称多处理(AM…

【阶段三】Python机器学习17篇:机器学习项目实战:随机森林回归模型

本篇的思维导图: 项目实战(随机森林回归模型) 项目背景 作为国家的支柱产业,房地产的稳定发展关乎国际民生。近几年,房地产价格飞速上涨,连创新高。在这种情况下研究房价的影响因素,具有重要的理论和现实意义。弄清影响房价的主要经济因素,探究我国房地产投资是…

calc()方法的使用

calc实现css中动态计算 环境 根据元素高度的变化,动态控制padding值 <marquee:name"up index"behavior"scroll"scrolldelay"30"direction"up"v-else-if"item.scrollPocition 3 ? true : false":style"{--positi…

核心乐理---五线谱基础

音符 音符的长度 音符的长度都是相对的定义 一般来说讲全音符设为一拍&#xff0c;但如果将二分音符设为一拍的话&#xff0c;全音符就是两拍 附点音符 可以在音符后跟上一个小点来改变音符的长度&#xff0c;如果一个音符后面跟了n个原点&#xff0c;它的长度就会变为原来的…

【学习记录】阿里云服务器+宝塔

环境&#xff1a;阿里云服务器&#xff0c;linux Centos系统 一、安装宝塔 参考链接&#xff1a;阿里云 ECS 服务器入门使用宝塔安装使用 1、进入云服务器控制台&#xff0c;打开远程连接 2、初次使用&#xff0c;可重置登录密码和连接密码 这是远程连接时需要输入的密码 这…

测试开发基础 | 计算机网络篇(二):物理层与数据链路层

计算机网络知识是自动化测试等技术基础&#xff0c;也是测试面试必考题目。霍格沃兹测试学院特别策划了本系列文章&#xff0c;将带大家一步步夯实计算机网络的基础知识。一、物理层由于物理层知识在互联网软件研发工作中用到的并不多&#xff0c;所以可以仅做一个简单的了解。…

Prompt: No !!!

Prompt is Not Good. Prompt 这个方法实际上并不是很好 Prompt 可以是in-context learning的一种。prompt-based learning has taken NLP by storm!! prompt-based的方法在NLP中引起风暴。但&#xff0c;Prompt-based的方法实际上不太行&#xff1a; 1. Inefficiency: The pro…

基于python的企业编码生成系统 (完整的代码+项目说明)

《企业编码生成系统》程序使用说明 在PyCharm中运行《企业编码生成系统》即可进入如图1所示的系统主界面。在该界面中可以选择要使用功能对应的菜单进行不同的操作。在选择功能菜单时&#xff0c;只需要输入对应的数字即可。 图1 系统主界面 具体的操作步骤如下&#xff1a;…