Go 反射机制,反射三定律!

简介: Go语言提供了一种机制,在运行时可以更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。

反射,官方对此有个非常简明的介绍:

  1. 反射提供一种让程序检查自身结构的能力
  2. 反射是困惑的源泉

静态类型

我们知道,Go是静态类型语言,例如”int”、”float32”、”[]byte”等等。每个变量在编译时就确定了自身的静态类型。

特殊的静态类型 interface

interface 类型是一种特殊的类型,它代表方法集合,可以用来存放任何实现了其方法的值。

最特殊的 interface 类型为空 interface 类型,即 interface {} ,interface用来表示一组方法集 合,所有实现该方法集合的类型都被认为是实现了该接口。所以空 interface 类型的方法集合为空,也就是说所有类型都可以认为是实现了该接口。

所以一个空interface类型变量可以存放所有值,这也是有些人认为Go是动态类型的原因,这是个错觉。

反射三定律

interface 类型有个(value,type)对,Go 提供了用来提取 interface 的 value 和 type 的方法,反射就是检查 interface 的这个(value, type)对的。

  • reflect.Type 提供一组接口处理 interface 的类型,即(value, type)中的 type。
  • reflect.Value 提供一组接口处理 interface 的值,即(value, type)中的 value。

反射第一定律:反射可以将 interface 类型变量转换成反射对象

通过反射获取一个变量的值和类型,示例:

package main
import (
    "fmt"
    "reflect"
)
func main(){
    var x float64 = 3.4
    t := reflect.TypeOf(x)
    fmt.Println("type:", t)
    v := reflect.ValueOf(x)
    fmt.Println("value", v)
}
复制代码

运行结果:

type: float64
value 3.4
复制代码

反射是针对 interface 类型的变量,TypeOf()ValueOf() 接受的参数都是 interface{} 类型的,即 x 值是被转成了 interface 传入的。

反射第二定律:反射可以将反射对象还原成 interface 对象

package main
import (
    "fmt"
    "reflect"
)
func main(){
    var x float64 = 3.4
    v := reflect.ValueOf(x) //v is reflext.Value
    var y float64 = v.Interface().(float64)
    fmt.Println("value", y)
}
复制代码

运行结果:

value 3.4
复制代码

对象 x 转换成反射对象 v,v 又通过 Interface() 接口转换成了 interface 对象,interface 对象通过.(float64)类型断言获取 float64 类型的值。

断言格式为:s = x.(T),意思是如果 x 所持有的元素如果同样实现了 T 接口,那么就把值传递给 s。

反射第三定律:反射对象可修改,value值必须是可设置的

通过反射可以将 interface 类型变量转换成反射对象,可以使用该反射对象设置其持有的值。

错误示例:

package main
import (
    "reflect"
)
func main(){
    var x float64 = 3.4
    v := reflect.ValueOf(x) //v is reflext.Value
    v.SetFloat(6.6) //Error
}
复制代码

上面程序会发生 panic ,原因即是 v 是不可修改的。

传入 reflect.ValueOf() 函数的其实是 x 的值,而非 x 本身。即通过 v 修改其值是无法影响 x 的,所以会报错。

如果构建 v 时使用 x 的地址就可实现修改了,但此时 v 代表的是指针地址,我们要设置的是指针所指向的内容,也即我们想要修改的是 *v 。 那怎么通过 v 修改 x 的值呢?

reflect.Value 提供了 Elem() 方法,可以获得指针指向的 value 。

示例:

package main
import (
    "fmt"
    "reflect"
)
func main(){
    var x float64 = 3.4
    v := reflect.ValueOf(&x)
    v.Elem().SetFloat(6.6)
    fmt.Println("x :", v.Elem().Interface())
}
复制代码

运行结果:

x : 6.6


相关文章
|
2月前
|
Go
Go to Learn Go之反射
Go to Learn Go之反射
41 8
|
3月前
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
42 4
|
4月前
|
安全 算法 程序员
在go语言中使用泛型和反射
【7月更文挑战第8天】本文介绍go支持泛型后,提升了代码复用,如操作切片、映射、通道的函数,以及自定义数据结构。 泛型适用于通用数据结构和函数,减少接口使用和类型断言。
124 1
在go语言中使用泛型和反射
|
3月前
|
存储 缓存 人工智能
深入理解 go reflect - 反射为什么慢
深入理解 go reflect - 反射为什么慢
35 0
|
3月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
54 0
|
3月前
|
JavaScript 前端开发 Go
Go中使用反射的动态方法调用
Go中使用反射的动态方法调用
|
5月前
|
Go
go反射获取变量类型、值、结构体成员、结构体方法
go反射获取变量类型、值、结构体成员、结构体方法
|
6月前
|
JSON 监控 安全
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
96 2
|
6月前
|
Go
go 反射Reflect
go 反射Reflect
|
6月前
|
Go 数据处理
【Go 语言专栏】Go 语言的反射机制及其应用
【4月更文挑战第30天】Go语言的反射机制通过`reflect`包实现,允许运行时检查和操作类型信息。核心概念包括`reflect.Type`(表示类型)和`reflect.Value`(表示值)。主要操作包括获取类型信息、字段信息及动态调用方法。反射适用于通用数据处理、序列化、动态配置和代码生成等场景,但也带来性能开销和维护难度,使用时需谨慎。通过实例展示了如何使用反射处理不同类型数据,强调了在理解和应用反射时需要不断实践。
50 0