揭秘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语言使用jwt认证的实现

    go语言使用jwt认证的实现

    本文主要介绍了go语言使用jwt认证的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • go mod 使用旧版本 版本号指定方式

    go mod 使用旧版本 版本号指定方式

    这篇文章主要介绍了go mod 使用旧版本 版本号指定方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Golang语言如何高效拼接字符串详解

    Golang语言如何高效拼接字符串详解

    最近在做性能优化,有个函数里面的耗时特别长,看里面的操作大多是一些字符串拼接的操作,而字符串拼接在 golang 里面其实有很多种实现,下面这篇文章主要给大家介绍了关于Golang语言如何高效拼接字符串的相关资料,需要的朋友可以参考下
    2021-11-11
  • Golang token的生成和解析详解

    Golang token的生成和解析详解

    这篇文章主要给大家介绍了Golang token的生成和解析,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-02-02
  • golang并发下载多个文件的方法

    golang并发下载多个文件的方法

    今天小编就为大家分享一篇golang并发下载多个文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • GoLang完整实现快速列表

    GoLang完整实现快速列表

    这篇文章主要介绍了GoLang完整实现快速列表,列表是一种非连续的存储容器,由多个节点组成,节点通过一些 变量 记录彼此之间的关系,列表有多种实现方法,如单链表、双链表等
    2022-12-12
  • Go语言Grpc Stream的实现

    Go语言Grpc Stream的实现

    本文主要介绍了Go语言Grpc Stream的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • go语言实现AES加密的方法

    go语言实现AES加密的方法

    这篇文章主要介绍了go语言实现AES加密的方法,实例分析了Go语言的加密技巧,需要的朋友可以参考下
    2015-03-03
  • 利用Golang生成整数随机数方法示例

    利用Golang生成整数随机数方法示例

    这篇文章主要介绍了利用Golang生成整数随机数的相关资料,文中给出了详细的介绍和完整的示例代码,相信对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-04-04
  • 关于golang类型推断和变量重声明详解

    关于golang类型推断和变量重声明详解

    在Go语言中,类型推断可以根据变量的初始化值自动推断出变量的类型,而不需要显式地声明变量类型,变量重声明可以在同一作用域内重复声明一个变量,只要其中至少一个是新的变量,本就简单的给大家介绍一下golang类型推断和变量重声明,需要的朋友可以参考下
    2023-08-08

最新评论