0%

go-reflect

反射简介

Go 在标准库中提供的 reflect 包让 Go 程序具备运行时的反射能力(reflection)。反射是程序在运行时访问、检测和修改它本身状态或行为的一种能力,各种编程语言所实现的反射机制各有不同。

Go 语言的 interface{}
类型变量具有析出任意类型变量的类型信息(type)和值信息(value)的能力,Go 的反射本质上就是利用 interface{}
的这种能力在运行时对任意变量的类型和值信息进行检视甚至是对值进行修改的机制。

反射让静态类型语言 Go 在运行时具备了某种基于类型信息的动态特性。利用这种特性,fmt.Println 在无法提前获知传入参数的真正类型的情况下依旧可以对其进行正确的格式化输出

反射三大法则

Rob Pike 还为 Go 反射的规范使用定义了三大法则,如果经过评估,你必须使用反射才能实现你要的功能特性,那么你在使用反射时需要牢记这三条法则。

  • 反射世界的入口:经由接口(interface {})类型变量值进入反射的世界并获得对应的反射对象(reflect.Value 或 reflect.Type)。
  • 反射世界的出口:反射对象(reflect.Value)通过化身为一个接口(interface {})类型变量值的形式走出反射世界。
  • 修改反射对象的前提:反射对象对应的 reflect.Value 必须是可设置的(Settable)。

reflect.TypeOf 和 reflect.ValueOf 是进入反射世界仅有的两扇 “大门”。通过 reflect.TypeOf 这扇 “门” 进入反射世界,你将得到一个 reflect.Type 对象,该对象中包含了被反射的 Go 变量实例的所有类型信息;

而通过 reflect.ValueOf 这扇 “门” 进入反射世界,你将得到一个 reflect.Value 对象。Value 对象是反射世界的核心,不仅该对象中包含了被反射的 Go 变量实例的值信息,而且通过调用该对象的 Type 方法,我们还可以得到 Go 变量实例的类型信息,这与通过 reflect.TypeOf 获得类型信息是等价的:

reflect.Value.Interface () 是 reflect.ValueOf ()
的逆过程,通过 Interface 方法我们可以将 reflect.Value 对象恢复成一个 interface {} 类型的变量值。这个离开反射世界的过程实质是将 reflect.Value 中的类型信息和值信息重新打包成一个 interface {} 的内部表示。

小结

reflect 包所提供的 Go 反射能力是一把 “双刃剑”,它既可以被用于优雅地解决一类特定的问题,但也会带来逻辑不清晰、性能问题以及难于发现问题和调试等困惑。
因此,我们应谨慎使用这种能力,在做出使用的决定之前,认真评估反射是不是问题的唯一解决方案;在确定要使用反射能力后,也要遵循上述三个反射法则的要求。

Powered By Valine
v1.5.2