揭秘Go语言中的反射机制

 更新时间:2023年10月25日 08:38:39   作者:Shasha^^  
在Go语言中,反射是通过reflect包来实现的,通过使用反射,我们可以在运行时获取对象的类型信息、访问对象的字段和方法、动态调用方法等,反射在很多场景下都非常有用,比如编写通用的代码、实现对象的序列化和反序列化、实现依赖注入等,需要的朋友可以参考下

基本概念

支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。

Go语言提供了 reflect 包来访问程序的反射信息。

Refelct解析

Refelct包 定义了两个重要的类型 Type 和 Value,任意接口在反射中都可以理解为 由 reflect.Type 和 reflect.Value 两部分组成 。简单来说,go 的接口是由两部分组成的,一部分是类型信息,另一部分是数据信息

eg

var a=1
var b interface{}=a

对于 这个例子,b 的类型信息是 int,数据信息是 1,这两部分信息都是存储在 b 里面的。b 的内存结构如下:

而 b实际上是一个空接口,也就是说一个 interface{} 中实际上既包含了变量的类型信息,也包含了类型的数据

refelct.Type ,refelct.Value
如上所说,所有的接口都含有type 和value ,我们可以使用refelct包中的 typeof 和valueof将信息从接口中取出

var a = 1
t := reflect.TypeOf(a)

var b = "hello"
t1 := reflect.ValueOf(b)

反射定律

三条反射定律 :

  1. 反射可以将 interface 类型变量转换成反射对象。
  2. 反射可以将反射对象还原成 interface 对象。
  3. 如果要修改反射对象,那么反射对象必须是可设置的(CanSet)。

将 interface 类型变量转换成反射对象

我们可以通过 reflect.TypeOf 和 reflect.ValueOf 来获取到一个变量的反射类型和反射值。

var a = 1
typeOfA := reflect.TypeOf(a)
valueOfA := reflect.ValueOf(a)

将反射对象还原成 interface 对象。

我们可以通过 reflect.Value.Interface 来获取到反射对象的 interface 对象,也就是传递给 reflect.ValueOf 的那个变量本身。 不过返回值类型是 interface{},所以我们需要进行类型断言。

i := valueOfA.Interface()
fmt.Println(i.(int))

修改反射对象

通过 reflect.Value.CanSet 来判断一个反射对象是否是可设置的。如果是可设置的,我们就可以通过 reflect.Value.Set 来修改反射对象的值。

var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println("settability of v:", v.CanSet()) // false
fmt.Println("settability of v:", v.Elem().CanSet()) // true

那什么情况下一个反射对象是可设置的呢?前提是这个反射对象是一个指针,然后这个指针指向的是一个可设置的变量 .

在上面这个例子中,v.CanSet() 返回的是 false,而 v.Elem().CanSet() 返回的是 true。

在这里,v是一根指针,但是v.Elem()才是v这根指针指向的值。Elem方法是一个解引用的作用。对于这个指针本身,我们修改它是没有意义的,我们可以设想一下, 如果我们修改了指针变量(也就是修改了指针变量指向的地址),那会发生什么呢?那样我们的指针变量就不是指向 x 了, 而是指向了其他的变量,这样就不符合我们的预期了。所以 v.CanSet() 返回的是 false。

而 v.Elem().CanSet() 返回的是 true。这是因为 v.Elem() 才是 x 本身,通过 v.Elem() 修改 x 的值是没有问题的


Elem()

refelct.Value中的Elem

reflect.Value 的 Elem 方法的作用是获取指针指向的值,或者获取接口的动态值。

对于指针很好理解,其实作用类似解引用。而对于接口,还是要回到 interface 的结构本身,因为接口里包含了类型和数据本身,所以 Elem 方法就是获取接口的数据部分(也就是 iface 或 eface 中的 data 字段)。

refelct.Type中的Elem

reflect.Type 的 Elem 方法的作用是获取数组、chan、map、指针、切片关联元素的类型信息,也就是说,对于 reflect.Type 来说, 能调用 Elem 方法的反射对象,必须是数组、chan、map、指针、切片中的一种,其他类型的 reflect.Type 调用 Elem 方法会 panic。

t1 := reflect.TypeOf([3]int{1, 2, 3}) // 数组 [3]int
fmt.Println(t1.String()) // [3]int
fmt.Println(t1.Elem().String()) // int

需要注意的是,如果我们要获取 map 类型 key 的类型信息,需要使用 Key 方法,而不是 Elem 方法。

m := make(map[string]string)
t1 := reflect.TypeOf(m)
fmt.Println(t1.Key().String()) // string

反射是Go语言中一种强大的编程技术,它允许程序在运行时动态地检查和修改对象的结构和行为。通过使用reflect包,我们可以在运行时获取对象的类型信息、访问对象的字段和方法、动态调用方法等。反射在很多场景下都非常有用,比如编写通用的代码、实现对象的序列化和反序列化、实现依赖注入等。然而,反射的使用也需要谨慎,因为它会带来一定的性能损耗。在实际开发中,我们应该根据具体的需求来判断是否需要使用反射。总的来说,反射是Go语言中一个非常有用的特性,它为我们提供了更大的灵活性和扩展性。

到此这篇关于揭秘Go语言中的反射机制的文章就介绍到这了,更多相关Go语言反射机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go语言通过odbc访问Sql Server数据库的方法

    go语言通过odbc访问Sql Server数据库的方法

    这篇文章主要介绍了go语言通过odbc访问Sql Server数据库的方法,实例分析了Go语言通过odbc连接与查SQL Server询数据库的技巧,需要的朋友可以参考下
    2015-03-03
  • Go语言开发必知的一个内存模型细节

    Go语言开发必知的一个内存模型细节

    这篇文章主要为大家介绍了Go语言开发必知的一个内存模型细节详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • go使用makefile脚本编译应用的方法小结

    go使用makefile脚本编译应用的方法小结

    makefile可以看作是make工具的脚本文件, 而make主要用来处理一系列命令。常用的比如用来编译和打包文件, 在C/C++的编译打包中应用最广泛了,这篇文章主要介绍了go使用makefile脚本编译应用,需要的朋友可以参考下
    2022-08-08
  • go语言中的面向对象

    go语言中的面向对象

    Go不支持类,而是提供了结构体。结构体中可以添加属性和方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。这篇文章介绍了go语言中的面向对象,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • golang实战之truncate日志文件详解

    golang实战之truncate日志文件详解

    这篇文章主要给大家介绍了关于golang实战之truncate日志文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • Go读取MySQL Date类型的避坑指南

    Go读取MySQL Date类型的避坑指南

    文章讨论了在Go中读取MySQL中的DATETIME和DATE类型数据时遇到的格式转换问题,并解释了parseTime参数的作用,并展示了在不同情况下,Go字段类型定义对读取到的时间字符串格式的影响,需要的朋友可以参考下
    2025-12-12
  • Go+Redis缓存设计与优化实现

    Go+Redis缓存设计与优化实现

    本文主要介绍了Go+Redis缓存设计与优化实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • Golang利用image包进行图像基础处理的深度指南

    Golang利用image包进行图像基础处理的深度指南

    在 Go 语言中,图像处理主要依赖标准库中的 image 包,它为开发者提供了一套统一的图像模型和接口,用于处理图片数据,下面小编就和大家详细介绍一下吧
    2026-03-03
  • go按行读取文件的三种实现方式汇总

    go按行读取文件的三种实现方式汇总

    最近有遇到需要用go读取文件的情况,下面这篇文章主要给大家介绍了关于go按行读取文件的三种实现方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Golang设计模式之生成器模式讲解和代码示例

    Golang设计模式之生成器模式讲解和代码示例

    生成器是一种创建型设计模式,使你能够分步骤创建复杂对象,与其他创建型模式不同,生成器不要求产品拥有通用接口,这使得用相同的创建过程生成不同的产品成为可能,本文就通过代码示例为大家详细介绍Golang生成器模式,感兴趣的同学可以参考下
    2023-06-06

最新评论