Go_反射的使用

2023-05-16

反射可以在运行时动态地检查变量和类型,并且可以调用变量和类型的方法、获取和修改变量的值和类型等。使用反射可以使程序更加灵活,但也需要谨慎使用

  1. 基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。
  2. 大量使用反射的代码通常难以理解。
  3. 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。

refect:

Go语言的反射主要涉及两个重要的类型:reflect.Typereflect.Value。其中,reflect.Type表示类型信息,包括类型的名称、方法和字段等;reflect.Value表示值信息,包括值的类型和内容等。通过这两个类型,我们可以对变量和类型进行各种反射操作。通过refect包下的reflect.TypeOf()reflect.ValueOf()两个函数来获取对象的ValueType

TypeOf():

reflect.TypeOf():用于获取变量的数据类型

func main() {
	// 获取x的数据类型,并赋值给t
	var x float64 = 3.14
	fmt.Println(reflect.TypeOf(x)) // float64
	fmt.Printf("%T", x)     // float64    Printf底层就是使用了反射,所以不管传入什么类型都能获取到
}

在反射中关于类型还划分为两种:

类型(Type):类型指的是变量的数据类型

种类(Kind):种类是语言原生数据类型,用于区分指针、结构体、数组、切片等类型。

type Myint int
type MyStruct struct {
	Name string
	age  int
}

var x Myint = 123
var y *Myint = &x
var z MyStruct = MyStruct{"itzhuzhu", 20}
var w *MyStruct = &z

func main() {
	
	fmt.Println(reflect.TypeOf(x)) // main.Myint
	fmt.Println(reflect.TypeOf(y)) // *main.Myint
	fmt.Println(reflect.TypeOf(z)) // main.MyStruct
	fmt.Println(reflect.TypeOf(w)) // *main.MyStruct
	
  // Kind:
	fmt.Println(reflect.TypeOf(x).Kind()) // int
	fmt.Println(reflect.TypeOf(y).Kind()) // ptr
	fmt.Println(reflect.TypeOf(z).Kind()) // struct
	fmt.Println(reflect.TypeOf(w).Kind()) // ptr
}

Kind源码:

// Kind表示类型所表示的特定类型
type Kind uint
const (
    Invalid Kind = iota  // 非法类型
    Bool                 // 布尔型
    Int                  // 有符号整型
    Int8                 // 有符号8位整型
    Int16                // 有符号16位整型
    Int32                // 有符号32位整型
    Int64                // 有符号64位整型
    Uint                 // 无符号整型
    Uint8                // 无符号8位整型
    Uint16               // 无符号16位整型
    Uint32               // 无符号32位整型
    Uint64               // 无符号64位整型
    Uintptr              // 指针
    Float32              // 单精度浮点数
    Float64              // 双精度浮点数
    Complex64            // 64位复数类型
    Complex128           // 128位复数类型
    Array                // 数组
    Chan                 // 通道
    Func                 // 函数
    Interface            // 接口
    Map                  // 映射
    Ptr                  // 指针
    Slice                // 切片
    String               // 字符串
    Struct               // 结构体
    UnsafePointer        // 底层指针
)

ValueOf():

reflect.ValueOf():用于获取变量的值

func main() {
	var x float64 = 3.14
	fmt.Println(reflect.ValueOf(x)) // 3.14
}

通过反射获取变量的值

func reflectValue(x interface{}) {
	v := reflect.ValueOf(x) // 获取接口x的值
	k := v.Kind()           // 获取接口x的种类
	switch k {
	case reflect.Int64:
		// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
		fmt.Printf("数据类型为Int64 值是:%d\n", int64(v.Int()))
	case reflect.Float32:
		// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
		fmt.Printf("数据类型为Float32 值是:%f\n", float32(v.Float()))
	case reflect.Float64:
		// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
		fmt.Printf("数据类型为Float64 值是:%f\n", float64(v.Float()))
	}
}
func main() {
	var a float32 = 3.14
	var b int64 = 100
	reflectValue(a) // 数据类型为Float32 值是:3.140000
	reflectValue(b) // 数据类型为Int64 值是:100

	/*
		在这里,reflect.ValueOf(10)的作用是将整数10转换为一个reflect.Value类型的值,
		该值包含整数10的类型和值等信息。具体来说,它返回一个reflect.Value类型的值c,
		该值表示整数10,并且可以使用反射获取它的类型和值等信息。通过使用reflect.ValueOf(),
		我们可以将一个普通的值转换为反射类型的值,从而使用反射对它进行进一步处理。
	*/
	c := reflect.ValueOf(10)
	fmt.Printf("c的数据类型是:%T\n", c) // c的数据类型是:reflect.Value
}

反射修改变量的值:

要修改变量的值,需要使用reflect.Value类型的Elem()方法获取到变量的指针,然后再使用reflect.Value类型的Set()方法修改值。

func main() {
	var x float64 = 3.14
	v := reflect.ValueOf(&x).Elem()
  // 也可以修改为其它数据类型,使用
	v.SetFloat(3.15)
	fmt.Println(x) // 3.15
}

isNil、isValid:

isNil():返回v的值是否为nil,v的值的类型必须是通道、函数、接口、映射、指针、切片之一,否则IsNil函数会导致panic。

func (v Value) IsNil() bool

IsValid():返回v是否有值,如果v是零值返回false,此时v除了IsValid、String、Kind之外的方法都会导致panic。

func (v Value) IsValid() bool

演示:

func main() {
	// *int类型空指针
	var a *int
	fmt.Println("a是否为空:", reflect.ValueOf(a).IsNil())
	// nil值
	fmt.Println("a是否有值:", reflect.ValueOf(a).IsValid())
	fmt.Println("测试传入nil值:", reflect.ValueOf(nil).IsValid())
	
	// 实例化一个匿名结构体
	b := struct{}{}
	// 判断结构体中是否有abc字段
	fmt.Println("struct是否存在abc字段:", reflect.ValueOf(b).FieldByName("abc").IsValid())
	// 判断结构体中是否有abc方法
	fmt.Println("struct是否存在abc方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
	
	// map
	c := map[string]int{}
	// 判断map中的key是否有abc
	fmt.Println("map中是否有abc这个key:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("abc")).IsValid())
}

输出:

a是否为空: true
a是否有值: true
测试传入nil值: false
struct是否存在abc字段: false
struct是否存在abc方法: false
map中是否有abc这个key: false

反射获取结构体信息:

方法说明
Field(i int) StructField根据索引,返回索引对应的结构体字段的信息
NumField() int返回结构体成员字段数量
NumMethod() int返回结构体方法的数量
Method(int) Method返回该类型的第i个方法
FieldByName(name string) (StructField, bool)根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool)根据传入的匹配函数匹配需要的字段。
MethodByName(string)(Method, bool)根据方法名返回该类型方法集中的方法

StructField类型用来描述结构体中的一个字段的信息。

type StructField struct {
	Name      string    // Name是字段的名字。
	PkgPath   string    // PkgPath是非导出字段的包路径,对导出字段该字段为""
	Type      Type      // 字段的类型
	Tag       StructTag // 字段的标签
	Offset    uintptr   // 字段在结构体中的字节偏移量
	Index     []int     // 用于Type.FieldByIndex时的索引切片
	Anonymous bool      // 是否匿名字段
}

演示:

type student struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func (s student) StudentMethod() {
	fmt.Println("我是student的方法")
}

func main() {
	stu := student{
		Name: "韩信",
		Age:  18,
	}

	t := reflect.TypeOf(stu)
	fmt.Println("根据索引返回字段信息:", t.Field(0))
	fmt.Println("结构体的名称:", t.Name())
	fmt.Println("结构体的类型:", t.Kind())
	fmt.Println("结构体的成员数量:", t.NumField())
	fmt.Println("结构体的方法数量:", t.NumMethod())
	fmt.Println("返回第i个方法:", t.Method(0))

	name, b := t.FieldByName("Name")
	fmt.Println("字段信息为:", name, "字段是否存在:", b)

	// 也可以使用if去判断,如果ok为true则执行
	if nameFiled, ok := t.FieldByName("Name"); ok {
		fmt.Println("字段信息为:", nameFiled, "字段是否存在:", ok)

	}

	// 通过for循环遍历结构体的所有字段信息

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Println(field.Name, field.PkgPath, field.Type, field.Tag.Get("json"), field.Index, field.Offset, field.Anonymous)
	}

	// 通过字段名获取指定结构体字段信息
	if field, ok := t.FieldByName("Name"); ok {
		fmt.Println(field.Name, field.PkgPath, field.Type, field.Tag.Get("json"), field.Index, field.Offset, field.Anonymous)
	}
}

输出:

根据索引返回字段信息: {Name  string json:"name" 0 [0] false}
结构体的名称: student
结构体的类型: struct
结构体的成员数量: 2
结构体的方法数量: 1
返回第i个方法: {StudentMethod  func(main.student) <func(main.student) Value> 0}
字段信息为: {Name  string json:"name" 0 [0] false} 字段是否存在: true
字段信息为: {Name  string json:"name" 0 [0] false} 字段是否存在: true
Name  string name [0] 0 false
Age  int age [1] 16 false
Name  string name [0] 0 false

演示:

type student struct {
	Name    string
	Age     int
	Address Address
}

type Address struct {
	City    string
	Country string
}

func (p *student) SayHello() {
	fmt.Printf("你好,我是野王 %s\n", p.Name)
}

func main() {
	p := student{
		Name: "韩信",
		Age:  30,
		Address: Address{
			City:    "野区",
			Country: "王者荣耀",
		},
	}

	// 使用 FieldByIndex 获取结构体中嵌套的字段值,返回第3个字段的第0个信息
	cityField := reflect.ValueOf(p).FieldByIndex([]int{2, 0})
	fmt.Println(cityField.Interface()) // 野区

	// 使用 FieldByNameFunc 根据字段名获取字段值
	ageFieldVal := reflect.ValueOf(p).FieldByNameFunc(func(name string) bool {
		return name == "Age"
	})
	if ageFieldVal.IsValid() {
		ageField := ageFieldVal.Interface().(int)
		fmt.Println(ageField) // 30
	}

	// 使用 MethodByName 调用结构体中的方法
	method := reflect.ValueOf(&p).MethodByName("SayHello")
	method.Call(nil) // 你好,我是野王 韩信
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go_反射的使用 的相关文章

  • Github+Hexo搭建个人博客(图文详解)

    文章目录 使用Github 43 hexo搭建个人博客 不会让小伙伴们走弯路 1 准备工作 xff1a 安装两个我们本次所需要使用的软件 xff1a 2 注册Github账号以及建立仓库 xff1a https github com htt
  • 2019-12-14-FTP服务器搭建

    title FTP服务器搭建 date 2019 12 14 15 34 19 updated 2019 12 14 15 34 19 categories 服务器 搭建 网络 tags FTP服务器 目录 什么是FTP服务器本地FTP服务
  • 小程序开发需要多少钱?

    小程序开发的费用 xff1a 一般几千到几万不等 看具体要求 其实开发小程序的价格主要取决于你要做多少页面和要做的页面和功能的复杂程度 如果是行业内比较成熟的标准化系统就会相对便宜点 至于开发多少钱 xff0c 这样看你采用以下哪种模式 x
  • Docker 1 - 基本使用

    Docker 文章目录 Docker一 关于 Docker安装 Docker1 查看版本2 安装3 卸载 Docker 常见命令查看Docker 磁盘使用情况清理磁盘停止Docker 服务 二 镜像查看已安装镜像拉取镜像删除镜像查找镜像方式
  • Spring MVC的异常处理和友好页面

    加油 xff0c 新时代打工人 xff01 Spring MVC详细环境配置和入门 Spring MVC 响应数据和结果视图 SpringMVC实现三种文件上传的方式 实现之前把Spring MVC环境配置完成 xff0c 参考以上文章 S
  • oh-my-posh 配置

    官网 oh my poshpowershell windows 安装 oh my posh winget span class token function install span JanDeDobbeleer OhMyPosh 初始化设
  • matplotlib 点线动画

    matplotlib 点线动画 作者的Github 主写 Web Development HTML PHP CSS JS node js Ruby Sketchup API Python Tkinter Django Matplotlib
  • Ocelot+Identity Server

    一 搭建一个ID4 IdServer NetCore API 认证服务器项目 1 1 在该项目中添加Nuget包 vs2 1版本安装IdentityServer4 2 5 3版本 1 2 在ID4 IdServer项目中新建一个Config
  • twitter输不了密码_如何更改您的Twitter密码

    twitter输不了密码 You should use a strong password for every important web service you have While Twitter may not be up there
  • 不忘初心牢记使命文化励志标语墙贴——个性一百剪纸

    不忘初心牢记使命手工剪纸 个性一百定制服务中心 xff0c 可快速定制各类家居家具用品 工艺礼品 图案造型 xff0c 提供从构思到设计到制作一条龙服务 xff0c 您敢想我们敢做 不忘初心牢记使命绘画贴纸 我们不局限于某种商品 xff0c
  • 升级系统的curl命令到最新版

    1 安装repo rpm Uvh http www city fan org ftp contrib yum repo rhel6 x86 64 city fan org release 2 1 rhel6 noarch rpm 2 查看该
  • Mybatis TooManyResultsException问题可能原因及解决方案

    Mybatis TooManyResultsException问题可能原因及解决方案 可能原因 配置文件的对应语句中使用了resultType而非resultMap对应的interface中的返回值并非使用List数据库问题 解决方案 更改
  • 一键关机 / 重启脚本

    关机 span class token shebang important bin bash span span class token builtin class name echo span span class token strin
  • chromium报错: ./chrome: error while loading shared libraries: *** 解决方法

    注 xff1a 笔者所使用系统为Ubuntu2004 首先推荐一个神奇网站 此网站可查询ubuntu所有缺少包的安装方法 一口气装所有缺少的包 xff1a apt install y libatk1 0 0 libatk bridge2 0
  • ubuntu 获取root权限超简单方法

    以下解决上述问题 1 打开终端ctrl 43 alt 43 T输入 sudo s 2 重新设置root密码 3 切换root用户登录 su root 如果之后安装软件因为权限问题 xff0c 直接切换并输入密码即可
  • Java异常详解及自定义异常

    我已经不用 try catch 处理异常了 xff01 太烦人了 51CTO博客 try catch处理什么异常 一 异常的概念 1 定义 xff08 什么是异常 xff1f xff09 异常是例外 xff0c 是一个程序在执行期间发生的事
  • Archlinux 安装、美化、软件入门(一)

    本文章可能更新不及时 xff0c 前往我的博客阅读 xff0c 阅读体验更佳 xff1a https jin dan oier space 前言 本文只适用于 UEFI 引导 本文适用于从 Windows 迁移到 Archlinux 本文作
  • Archlinux 安装、美化、软件入门(二)

    本文章可能更新不及时 xff0c 前往我的博客阅读 xff0c 阅读体验更佳 xff1a https jin dan oier space 0x01 备份数据 使用 微PE 和 DiskGenius 工具进行数据备份和分区克隆 由于在 PE
  • Archlinux 安装、美化、软件入门(三)

    本文章可能更新不及时 xff0c 前往我的博客阅读 xff0c 阅读体验更佳 xff1a https jin dan oier space 0x03 安装系统 从U盘启动 待完善 连接Wi Fi xff08 有线用户直接跳过 xff09 输
  • 如何在笔记本电脑触摸板上进行中键单击

    Most laptop touchpads make it possible to perform a middle click but not all do In some situations you may need to enabl

随机推荐

  • Archlinux 安装、美化、软件入门(五)

    本文章可能更新不及时 xff0c 前往我的博客阅读 xff0c 阅读体验更佳 xff1a https jin dan oier space 0x07 系统美化 GRUB 美化 挑选喜欢的主题并下载 在 Gnome look 网站可以找到 G
  • 系统提示E: Unable to correct problems, you have held broken packages.错误解决

    系统提示E Unable to correct problems you have held broken packages 上网求答 xff0c 发现网上很多人选择用 aptitude xff0c 但是这个一般比较危险 xff0c 并不推
  • Linux kernel升级(详)

    环境 xff1a 系统版本 xff1a CentOS Linux release 7 4 1708 Core 内核版本 xff1a 3 10 0 693 el7 x86 64 内核由3 10升级5 4 升级说明 xff1a Linux Ke
  • stm32之蓝牙模块HC-05使用

    参考资料 xff1a 常用模块 HC 05蓝牙串口通信模块使用详解 xff08 实例 xff1a 手机蓝牙控制STM32单片机 xff09 HC 05蓝牙模块使用教程 HC 05蓝牙模块使用记录 补充与理解 xff1a 套餐有两个板子 一个
  • 2021-09-28->HttpClientUtil 工具包

    span class token keyword package span span class token namespace com span class token punctuation span ruoyi span class
  • 微信通知方法

    span class token comment 微信通知 span span class token function wxMessage span span class token punctuation span userId spa
  • 2021-09-29->微信支付

    接下来看微信支付jsapi接口是怎么调用的 步骤一 xff1a 获取微信支付四大参数 首先要想支持微信支付 必须拥有两个账号 微信公众平台 xff1a 账户 公众APPID xff0c APPSECEPT xff0c 微信商户平台商户ID
  • 20以为加减法

    span class token keyword for span span class token punctuation span span class token keyword int span i span class token
  • coturn服务配置

    COTURN服务配置 准备工作 一台带有公网ip的服务器 xff08 coturn服务部署在具有公网ip的服务器上 xff09 下载coturn wget https span class token operator span span
  • BigDecimal 精确算法 工具类

    BigDecimal a 61 new BigDecimal 101 BigDecimal b 61 new BigDecimal 111 使用compareTo方法比较 注意 xff1a a b均不能为null xff0c 否则会报空指针
  • wsappx_什么是“ wsappx”,为什么在我的PC上运行它?

    wsappx The wsappx process is part of Windows 8 and 10 and you may see it running in the background or even using a signi
  • python获取文件路径、文件夹内所有文件

    python获取文件路径 文件夹内所有文件名字 项目内相对路径 在test12 py内 想获取其所在文件夹的所有的表格文件 windows 第一种方案 34 34 34 获取路径 34 34 34 def list dir file dir
  • MySQL-常用内置函数(字符串、数值、日期、流程)

    字符串函数 xff1a 函数作用CONCAT str1 str2 拼接字符串 xff0c 返回拼接后的字符串LENGTH str 返回字符串字节长度 xff0c 注意是字节 xff0c 中文占3个LEFT str len 返回从最左边截取到
  • MySQL-基础语法DDL、DML、DQL、DCL

    DDL xff1a DDL Data Definition Language 数据库定义语言用来定义数据库对象 xff1a 数据库 xff0c 表 xff0c 列等 关键字 xff1a create drop alter 等 语法 DML语
  • VMware安装虚拟机Mac版

    VMware xff1a 1 不需要分区或重开机就能再同一台PC上使用多种操作系统 2 完全隔离并且保护不同操作系统的环境以及所有软件 资料 3 不同的操作系统之间还能互动操作 4 有复原功能 5 能够设置并且随时修改操作系统的操作环境 下
  • JSON转换工具

    JSON的处理 xff1a JSON JavaScript Object Notation xff1a 是一种轻量级的数据交换格式 它是基于 ECMAScript 规范的一个子集 xff0c 采用完全独立于编程语言的文本格式来存储和表示数据
  • MacOS Apple M1 安装ARM架构的JDK及动态切换版本

    JDK下载安装 xff1a 咱就是说 xff0c ARM版本的JDK就是一个字 xff0c 真特么快 xff0c 想变快吗 xff0c 赶紧下载叭 xff01 xff01 1 下载地址 xff1a https www azul com do
  • Go_详解TCP协议三次握手四次挥手

    三次握手 xff1a 三次握手表示建立通信阶段 xff0c 在TCP协议中 xff0c 在发送数据的准备阶段 xff0c 客户端与服务器之间的三次交互 xff0c 以保证连接的可靠 xff0c 由于这种面向连接的特性 xff0c TCP协议
  • Go_常量、iota(枚举)的使用

    常量 常量是在程序运行过程中 xff0c 其值不可以发生改变的数据 xff0c 常量无法被获取地址 常量中的数据类型能是布尔型 数字型 xff08 整型 浮点型和复数型 xff09 字符串 常量的定义是通过const关键字完成的 xff0c
  • Go_反射的使用

    反射可以在运行时动态地检查变量和类型 xff0c 并且可以调用变量和类型的方法 获取和修改变量的值和类型等 使用反射可以使程序更加灵活 xff0c 但也需要谨慎使用 基于反射的代码是极其脆弱的 xff0c 反射中的类型错误会在真正运行的时候