iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >一文详解Golang中的反射
  • 789
分享到

一文详解Golang中的反射

反射go语言Golang 2023-05-14 19:05:29 789人浏览 独家记忆
摘要

本篇文章带大家主要来聊聊golang中反射,希望对你有新的认知。虽然很多人使用 Go 语言有一定时间了,甚至有的使用了 1 年 2 年,然后对于 Go 语言中的反射还是模棱两可,使用起来的时候,心里也不是非常有底气。【相关推荐:Go视频教程

本篇文章带大家主要来聊聊golang反射,希望对你有新的认知。

一文详解Golang中的反射

虽然很多人使用 Go 语言有一定时间了,甚至有的使用了 1 年 2 年,然后对于 Go 语言中的反射还是模棱两可,使用起来的时候,心里也不是非常有底气。【相关推荐:Go视频教程编程教学】

更有甚者,几乎不使用反射,当然,也不是什么错,在工作中能用最简单最高效,又可扩展,性能还好的方式来进行处理自然是最 nice ,没有必要去生搬硬套一些高级用法,毕竟工作不是我们的试炼场,可以自己下来多多实验,本次就来好好看看如何去玩反射

文章分别从如下五个方面来聊

  • 反射是什么
  • 反射的规则
  • 使用案例并灵活运用
  • 反射原理

简单来看反射是什么

简单来看,反射就是在程序运行时期对程序本身进行访问和修改的能力,例如在程序运行时,可以修改程序的字段名称,字段值,还可以给程序提供接口访问的信息等等

这是 Go 语言中提供的一种机制,我们可以在 Go 语言公共库中可以看到很多关于 reflect 的使用位置

例如常用的 fmt 包,常用的 json 序列化和反序列化,自然前面我们说到的 gORM 库自然也是使用了反射的

可是我们一般为什么要使用反射呢?

根据反射的能力,自然是因为我们提供的接口并不知道传入的数据类型会是什么样的, 只有当程序运行的时候才知道具体的数据类型

但是我们编码的时候又期望去校验程序运行时传入的类型会是什么样的(例如 JSON 的序列化)并对其这种具体的数据进行操作,这个时候,咱们就需要用到反射的能力了

所以对于使用到反射的地方,你都能看到 interface{} 是不是就不奇怪了呢?

正是因为不确定传入的数据类型会是什么样的,所以才设计成 interface{} ,那么如果对于 interface 有还不清楚他的特点和使用方式的,可以查看历史文章:

  • 关于 interface{} 会有啥注意事项?上
  • 关于 interface{} 会有啥注意事项?下

先关注反射的规则

首先关注反射的三个重要的定律,知道规则之后,我们按照规则玩就不会有什么问题,只有当我们不清楚规则,总是触发条款的时候,才会出现奇奇怪怪的问题

  • 反射是可以将 接口类型的变量 转换成 反射类型的对象

  • 反射可以将 反射类型的对象 转换成 接口类型的变量
  • 我们在运行时要去修改的 反射类型的对象 ,那么要求这个对象对应的值是要可写的

对于上述 3 个规则也是比较好理解,还记的之前我们说过的 unsafe 包里面的指针吗?

都是将我们常用的数据类型,转换成包(例如 unsafe包,或者 reflect 包)里面的指定数据类型,然后再按照包里面的规则进行修改数据

相当于,换个马甲,就可以进行不同的操作了

关注使用案例并灵活运用

一般咱们先会基本的应用,再去研究他的原理,研究他为什么可以这样用,慢慢的才能理解的更加深刻

对于定律一,将 接口类型的变量 转换成 反射类型的对象

实际上此处说的 接口类型的变量 我们可以传入任意数据类型的变量,例如 int, float, string ,map, slice, struct 等等

反射类型的对象 这里就可以理解成 reflect 反射包中的 reflect.Type reflect.Value 对象,可以通过 reflect 包中提供的 TypeOfValueOf 函数得到

其中 reflect.Type 实际上是一个 interface ,他里面包含了各种接口需要进行实现,它里面提供了关于类型相关的信息

其中如下图可以查看到 reflect.Type 的所有方法,其中

  • 绿色的 是所有数据类型都是可以调用的
  • 红色的是 函数类型数据可以调用的
  • 黑色的是 Map,数组 Array,通道 Chan,指针 Ptr 或者 切片Slice 可以调用的
  • 蓝色的是结构体调用的
  • 黄色的是通道 channel 类型调用的

reflect.Value 实际上是一个 struct,根据这个 struct 还关联了一组方法,这里面存放了数据类型和具体的数据,通过查看其数据结构就可以看出

type Value struct {
   typ *rtype
   ptr unsafe.Pointer
   flag
}

看到此处的 unsafe.Pointer 是不是很熟悉,底层自然就可以将 unsafe.Pointer 转换成 uintptr,然后再修改其数据后,再转换回来,对于 Go 指针不太熟悉的可以查看这篇文章:

  • GO 中的指针?

写一个简单的 demo 就可以简单的获取到变量的数据类型和值

func main() {   var demoStr string = "now reflect"
   fmt.Println("type:", reflect.TypeOf(demoStr))
   fmt.Println("value:", reflect.ValueOf(demoStr))
}

对于定律二,将 反射类型的对象 转换成 接口类型的变量

我们可以通过将 reflect.Value 类型转换成我们具体的数据类型,因为 reflect.Value 中有对应的 typ *rtype 以及 ptr unsafe.Pointer

例如我们可以 通过 reflect.Value 对象的 interface() 方法来处理

func main() {   var demoStr string = "now reflect"
   fmt.Println("type:", reflect.TypeOf(demoStr))
   fmt.Println("value:", reflect.ValueOf(demoStr))   var res string
   res = reflect.ValueOf(demoStr).Interface().(string)
   fmt.Println("res == ",res)
}

对于定律三,修改反射类型的对象

首先我们看上书的 demo 代码,传入 TypeOfValueOf 的变量实际上也是一个拷贝,那么如果期望在反射类型的对象中修改其值,那么就需要拿到具体变量的地址然后再进行修改,前提是这个变量是可写的

举个例子你就能明白

func main() {
   var demoStr string = "now reflect"
   v := reflect.ValueOf(demoStr)
   fmt.Println("is canset ", v.CanSet())
   //v.SetString("hello world")   // 会panic
   }

可以先调用 reflect.Value 对象的 CanSet 查看是否可写,如果是可写的,我们再写,如果不可写就不要写了,否则会 panic

那么传入变量的地址就可以修改了??

传入地址的思路没有毛病,但是我们去设置值的方式有问题,因此也会出现上述的 panic 情况

此处仔细看能够明白,反射的对象 v 自然是不可修改的,我们应该找到 reflect.Value 里面具体具体的数据指针,那么才是可以修改的,可以使用 reflect.Value Elem 方法

稍微复杂一点的

看上了上述案例可能会觉得那么简单的案例,一演示就 ok,但是工作中一用就崩溃,那自然还是没有融会贯通,说明还没有消化好,再来一个工作中的例子

  • 一个结构体里面有 map,map 中的 key 是 string,value 是 []string
  • 需求是访问 结构体中 hobby 字段对应的 map key 为 sport 的切片的第1 个元素,并将其修改为 hellolworld
type RDemo struct {
   Name  string
   Age   int
   Money float32
   Hobby map[string][]string
}

func main() {
   tmp := &RDemo{
      Name:  "xiaomiong",
      Age:   18,
      Money: 25.6,
      Hobby: map[string][]string{
         "sport": {"basketball", "football"},
         "food":  {"beef"},
      },
   }

   v := reflect.ValueOf(tmp).Elem()  // 拿到结构体对象
   h := v.FieldByName("Hobby")    // 拿到 Hobby 对象
   h1 := h.MapKeys()[0]    // 拿到 Hobby 的第 0 个key
   fmt.Println("key1 name == ",h1.Interface().(string))

   sli := h.Mapindex(h1)    // 拿到 Hobby 的第 0 个key对应的对象
   str := sli.Index(1)      // 拿到切片的第 1 个对象
   fmt.Println(str.CanSet())

   str.SetString("helloworld")
   fmt.Println("tmp == ",tmp)
}

可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致的,此处也提醒一波,使用 map 的时候要注意这一点

看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢

有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式

反射原理

那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic

TypeOf

来看 TypeOf 的接口中涉及的数据结构

在 reflect 包中 rtype 是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下

// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
   size       uintptr
   ptrdata    uintptr
   hash       uint32 
   tflag      tflag
   align      uint8
   fieldAlign uint8
   kind       uint8
   equal     func(unsafe.Pointer, unsafe.Pointer) bool
   GCdata    *byte 
   str       nameOff
   ptrToThis typeOff
}

其中可以看到此处的 rtype 的结构保持和 runtime/type.go 一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容

ValueOf

ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构

// emptyInterface is the header for an interface{} value.type emptyInterface struct {
   typ  *rtype
   Word unsafe.Pointer
}复制代码

emptyInterface 结构中有 rtype 类型的指针, word 自然是对应的数据的地址了

reflect.Value 对象中的方法也是非常的多,用起来和上述说到的 reflect.Type 接口中的功能类似

关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好

殊不知,此处的 reflect.Value 也是可以转换成 reflect.Type ,可以查看源码中 reflect\value.gofunc (v Value) Type() Type {

其中 reflect.Valuereflect.Type ,和任意数据类型 可以相互这样来转换

如下图:

总结

至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点

关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的

高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧

大道至简,反射三定律,活学活用

以上就是一文详解Golang中的反射的详细内容,更多请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: 一文详解Golang中的反射

本文链接: https://www.lsjlt.com/news/203439.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文详解Golang中的反射
    本篇文章带大家主要来聊聊Golang中反射,希望对你有新的认知。虽然很多人使用 Go 语言有一定时间了,甚至有的使用了 1 年 2 年,然后对于 Go 语言中的反射还是模棱两可,使用起来的时候,心里也不是非常有底气。【相关推荐:Go视频教程...
    99+
    2023-05-14
    反射 go语言 Golang
  • Golang 中反射的应用实例详解
    目录引言Golang类型设计原则Golang 中为什么要使用反射/什么场景可以(应该)使用反射举例场景:反射的基本用法反射的性能分析与优缺点测试反射结构体初始化测试结构体字段读取/赋...
    99+
    2024-04-02
  • 一文带你了解Golang中reflect反射的常见错误
    目录获取 Value 的值之前没有判断类型没有传递指针给 reflect.ValueOf在一个无效的 Value 上操作什么时候 IsValid 返回 false其他情况下 IsVa...
    99+
    2023-01-05
    Golang reflect反射错误 Golang reflect反射 Golang reflect
  • Golang泛型与反射的应用详解
    目录1. 泛型1.1 定义1.2 例子1.3 自定义泛型类型1.4 泛型与switch结合使用1.5 泛型实战2. 反射2.1 定义2.2 方法2.3 反射读取2.4 反射操作2.5...
    99+
    2024-04-02
  • 一文带你掌握Golang的反射基础
    目录什么是反射go的反射实战reflect包中常用的几个类型和方法什么是反射 反射的机制是在运行时可以获取到其变量的类型和值,且可以在运行时对其变量类型和值进行检查,可以对其值进行修...
    99+
    2023-02-17
    Golang反射基础 Golang反射 Go 反射
  • 一文搞懂Spring中的注解与反射
    目录前言一、内置(常用)注解1.1@Overrode1.2@RequestMapping1.3@RequestBody1.4@GetMapping1.5@PathVariable1....
    99+
    2024-04-02
  • 一文搞懂Java中的注解和反射
    目录1、注解(Annotation)1.1 什么是注解(Annotation)1.2 内置注解1.3 元注解(meta-annotation)1.4 自定义注解2、反射(Reflec...
    99+
    2024-04-02
  • Java中的反射机制详解
    目录一、什么是反射?二、为什么要用反射三、Class类四、获取Class类对象的四种方式五.通过反射构造一个类的实例①使用Class.newInstance②通过反射先获取构造方法再...
    99+
    2024-04-02
  • Golang学习之反射机制的用法详解
    目录介绍TypeOf() ValueOf()获取接口变量信息事先知道原有类型的时候事先不知道原有类型的时候介绍 反射的本质就是在程序运行的时候,获取对象的类型信息和内存结构,反射是把...
    99+
    2024-04-02
  • 一文搞懂Java中的反射机制
    目录一. 反射的概念二. 为什么需要反射三. 反射的基石四. 反射的实现1. 获取字节码文件对象 2. 反射的使用 反射的优缺点 一. 反射的概念 Ja...
    99+
    2024-04-02
  • golang函数的反射
    go 中的函数反射提供了获取和操作函数信息并动态调用的能力。通过 reflect.valueof 可获得函数反射对象,包含其签名、参数和返回值信息。要动态调用,可构建参数反射值列表,并通...
    99+
    2024-04-21
    golang 反射
  • Golang使用反射的动态方法调用详解
    Go是一种静态类型的语言,提供了大量的安全性和性能。静态类型的好处之一是,编译器可以在编译时捕获错误,防止在运行时发生。 在Go中,方法是用语法func(receiverName r...
    99+
    2023-03-14
    Golang反射动态方法调用 Golang反射动态方法 Golang反射
  • 一文带你了解java中的泛型、反射与注解
    一文带你了解java中的泛型、反射与注解?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。泛型擦除泛型擦除: 泛型只在编译时期有效,编译后的字节码文件中不存在泛型信息。声明泛型...
    99+
    2023-05-31
    java 反射 泛型
  • C#反射(Reflection)详解
    目录一.System.Type类1.Type属性2.方法二.Assembly类1.获取在程序集中定义的类型的详细信息2.获取自定义特性的详细信息1.编写自定义特性2.对VectorC...
    99+
    2024-04-02
  • Java 反射(Reflect)详解
    目录一 首先我们的去知道什么是反射?二(刨根问底)知道是什么还需要知道什么“成分”组成反射?2.1 Class 对象的获取及使用2.2 拿到碗筷就得去盛饭,拿到了Class就得去操作...
    99+
    2024-04-02
  • 一文详解Golang中的方法
    Golang(也被称为Go)是一种并发编程语言,它是由谷歌公司开发的。Golang很流行,因为它的代码简洁、易读并且能够处理高并发。一个Golang程序在编写时包含有函数和方法,本文将会关注Golang的方法。方法是面向对象编程中的关键部分...
    99+
    2023-05-14
    Golang go语言
  • 深入了解Golang中reflect反射的使用
    目录1. 介绍2. 方法示例2.1 通过反射获取对象的键(类型)和值2.2 反射对象的类型和属性3. 反射对Json的操作3.1 反射与Json属性解析3.2 Json包的序列化与反...
    99+
    2023-05-20
    Golang reflect反射 Go reflect反射 Golang reflect
  • java反射详解(3)
    动态代理 【案例】首先来看看如何获得类加载器:  class test{       } class hello{     public static void main(String[] args) {         test t=ne...
    99+
    2023-01-31
    反射 详解 java
  • 一文详解golang中的注释
    Golang是一种编程语言,它有着比较高的代码可读性和简洁性。然而,在编写代码时,总有些地方需要添加注释来帮助解释某些细节或者增加代码的可读性。在这篇文章中,我们将介绍一些关于Golang注释的内容。一、单行注释单行注释是在代码行的末尾添加...
    99+
    2023-05-14
    go语言 Golang 注释
  • 详解PHP反射API
    PHP中的反射API就像Java中的java.lang.reflect包一样。它由一系列可以分析属性、方法和类的内置类组成。它在某些方面和对象函数相似,比如get_class_vars(),但是更加灵活,而且可以提供更多信息。反射API也可...
    99+
    2023-09-02
    php 开发语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作