
Go的反射基础是接口和类型系统。Go的反射借助了实例到接口的转换所使用的的数据结构,首先将实例传递给内部的空接口,实际上是将一个实例类型转换为接口可以表述的数据结构eface ,反射基于这个转换后的数据结构来访问和操作实例的值和类型。
在Golang的实现中,每个interface变量都有一个对应的pair,pair中记录了实际变量的值和类型:
(value, type)
value是实际变量值,type是实际变量的类型。一个interface{}类型的变量包含两个指针,一个指针指向值的类型(对应concrete type),另外一个指针指向实际的值(对应value)。
interface及其pair的存在,是Golang中实现反射的前提,理解pair,就更容易理解反射。反射就是用来检测存储在接口变量内部(值value, 类型concrete type)pair对的一种机制。
反射规则实例、value、type三者之间的转换关系如图所示
从实例到 Value
通过实例获取Value对象,直接使用 reflect.ValueOf() 函数。例如
func ValueOf(i interface{}) Value
var x int = 100
v := reflect.ValueOf(x)
从实例到 Type
通过实例获取反射对象的Type,直接使用 reflect.TypeOf() 函数。例如
func TypeOf(i interface{}) Type
var x int = 100
v := reflect.TypeOf()
从 Type 到 Value
Type里边只有类型信息,所以直接从一个 Type 接口变量里边是无法获得实例的 Value 的,但可以通过该 Type 构建一个新实例的 Value。reflect 包提供两种方法,
// New返回的是一个Value,该Value的type为PtrTo(typ),即Value的Type是指定的typ的指针类型 func New(typ Type) Value // Zero 返回的是一个 typ 类型的零值,注意返回的 Value 不能寻址,值不可改变 func Zero(typ Type) Value // 如果知道一个类型值的底层存放地址,则还有一个函数可以根据type和该地址值恢复出Value的 func NewAt(typ Type, p unsafe.pointer) Value
从 Value 到 Type
从反射对象 Value 到 Type 可以直接调用 Value 的方法,因为 Value 内部存放着到 Type 类型的指针。
func (v Value) Type() Type
从 Value 到实例
Value 本身就包含类型和值信息,reflect 提供了丰富的方法来实现从 Value 到实例的转换
// 该方法最通用,用来将 Value 转换为空接口,该空接口内部存放具体类型实例
// 可以使用接口类型查询去还原为具体的类型
func (v Value) Interface() (i interface{})
//Value 自身也提供丰富的方法,直接将 Value 转换为简单的类型实例, 如果类型不匹配,则直接引起panic
func (v Value) Bool() bool
func (v Value) Float() float64
func (v Value) Int() int64
func (v Value) Uint() uint64
例如
var x int = 123 v := reflect.ValueOf(x) fmt.Println(x.Int()) // 123 值得注意的是类型首字母要大写,如 x.Int
从 Value 的指针到值
从一个指针类型的 Value 获得值类型 Value 有两种方法
// 如果 v类型是接口,则 Elem()返回接口绑定的实例的Value // 如果 v类型是指针,则返回指针的 Value,否则引起 painc func (v Value) Elem() Value // 如果 v 是指针,则返回指针值的 Value,否则返回 v 自身,该函数不会引起 panic func Indirect(v Value) Value
Type 指针和值的相互转换
1)指针类型Type到值类型Type
// t 必须是Array、Chan、Map、Ptr、Slice,否则会引起panic // Elem返回的是其内部元素的Type t.Elem() Type
2)值类型Type到指针类型Type
// PtrTo 返回的是指向t 的指针型Type func PtrTo(t Type) Type
Value值的可修改性
Value 值的修改涉及如下两个方法
// 通过CanSet判断是否能修改 func (v Value) CanSet() bool // 通过Set进行修改 func (v Value) Set(x Value)
但是修改值一般要配合指针使用.
例如:
var x = 10 v := reflect.ValueOf(&x) v.Elem().SetInt(20) //v.Elem().Kind == int fmt.Println(x) // 20
关于结构体的一些方法
| 方法 | 说明 |
|---|---|
| Field(i int) StructField | 根据索引返回对应的结构体字段信息 |
| NumField() int | 返回结构体成员字段数量 |
| FieldByName(name string) (StructField, bool) | 根据名字搜索是否有对应结构体字段信息并返回 |
| FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息 |
| FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据传入的匹配函数匹配需要的字段 |
| NumMethod() int | 返回结构体方法数量 |
| Method(i int) Method | 返回第i个方法 |
| MethodByName(string)(Method, bool) | 根据方法名返回方法 |