TOC

1. 什么是reflect?

反射是指在运行期对程序本身进行访问和修改的能力。程序编译后,变量被转换为内存地址,而变量名无法被编译器写入可执行部分。在运行程序时,程序无法获取自身的信息。

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

反射:反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力

Go使用reflect包访问程序的反射信息。
  • 支持反射的语言:Go、Java、C#,而C/C++没有反射功能。
  • Lua,JavaScript动态语言,可以在运行期访问程序自身的值与类型,故不需要反射特性

Go提供了一种在运行时更新和检查变量的值、调用变量的方法的机制,但在编译器不知道这些变量的具体类型,这种机制被称为反射

2. reflect 使用场景

示例1:
示例2:
interface类型值

3. reflect 实现原理

interface

3.1 反射的基础: interface{}

interface{} 存储结构

go 的接口是由两部分组成的,一部分是类型信息,另一部分是数据信息。

bint1bb
befacesrc/runtime/runtime2.go
一个 interface{} 中实际上既包含了变量的类型信息,也包含了类型的数据。

3.2 反射对象 reflect.Type & reflect.Value

  • reflect.TypeOf :返回反射类型(returns the reflection Type that represents the dynamic type of i)
  • reflect.ValueOf:返回反射值(returns a new Value initialized to the concrete value)
接口类型变量反射类型对象
reflect.TypeOf() 源码:
reflect.ValueOf() 源码:
TypeOfValueOfinterface{}reflect.Typereflect.Valuereflect.TypeOfreflect.ValueOf

3.3 反射定律

在 go 官方博客中关于反射的文章 laws-of-reflection 中,提到了三条反射定律:

interfaceinterfaceCanSet

关于这三条定律,官方博客已经有了比较完整的阐述,感兴趣的可以去看一下官方博客的文章。这里简单阐述一下:

interface

上文中举的例子中已经有了很清楚的表示,就不再举例了。

interface
reflect.Value.Interfaceinterfacereflect.ValueOfinterface{}
CanSet
reflect.Value.CanSetreflect.Value.Set

可设置要求:

  1. 反射对象是一个指针
  2. 这个指针指向的是一个可设置的变量

原因:

如果这个值只是一个普通的变量,这个值实际上被拷贝了一份。如果通过反射修改这个值,那么实际上是修改的这个拷贝的值,而不是原来的值。 所以 go 语言在这里做了一个限制。

v.CanSet() == false
vv.Elem()
x
v.CanSet()false

示例

4. 常用的方法

4.1 Elem

Elemelementreflectreflect.Type的Elemreflect.Value

reflect.Type 的 Elem 方法

reflect.TypeElemreflect.Type
Elemreflect.TypeElempanic
mapkeyKeyElem

reflect.Value 的 Elem 方法

reflect.ValueElem

上文中,修改反射对象章节中,有具体例子。

4.2 Interface 方法

获取反射对象的动态值。 也就是说,如果反射对象是一个指针,那么 Interface 方法会返回指针指向的值。

4.3 Kind 方法

Kind 表示的是 go 底层类型系统中的类型。

Kind
reflect.TypeKind

4.4 Type方法

reflect.Type

5. Reflect性能

首先我们来做一组测试:

我们可以通过以下的 benchmark 来对比一下:

addAny()addInt64()

慢的原因:

反射interface{}FieldByName

6. 总结

reflectinterface{}interface{}reflect.Typereflect.Valuereflect.Typereflect.ValueinterfaceinterfaceCanSetreflect.Valuereflect.TypeElemreflect.TypeElemarray、chan、map、pointerslicereflect.Typereflect.ValueElemreflect.Valuereflect.ValueInterfaceinterface{}TypeKindTypeKindint、string、struct

7 参考文献