go语言-反射的基本函数

反射的基本函数

	Go 语言 reflect 包里定义了一个接口和一个结构体,即 reflect.Type 和 reflect.Value,它们提
供很多函数来获取存储在接口里的类型信息。前者主要提供关于类型相关的信息,所以它和 _type 
关联比较紧密;后者则结合 _type 和 data 两者,因此程序员可以获取甚至改变类型的值。
	并且,reflect 包提供了两个基础的关于反射的函数,来获取上述的接口和结构体:
// src/reflect/type.go 
func TypeOf(i interface{}) Type 
// src/reflect/value.go 
func ValueOf(i interface{}) Value
	TypeOf 函数用来提取一个接口中值的类型信息。由于它的输入参数是一个空的 interface{},
调用此函数时,实参会先被转化为 interface{}类型。这样,实参的类型信息、方法集、值信息都存
储到 interface{} 变量里了。

来看下源码:

// src/reflect/type.go 
func TypeOf(i interface{}) Type { 
   eface := *(*emptyInterface)(unsafe.Pointer(&i)) 
   return toType(eface.typ) 
}
	这里的 emptyInterface 和前面提到的 eface 是一回事(字段名略有差异,字段类型是相同
的),存在在不同的源码包:前者在 reflect 包,后者在 runtime 包,eface.typ 就是动态类型。
type emptyInterface struct { 
    typ *rtype 
    word unsafe.Pointer 
}

至于 toType 函数,只是做了一个类型转换:

func toType(t *rtype) Type { 
    if t == nil { 
        return nil 
    } 
    return t 
}

注意,返回值 Type 实际上是一个接口,它定义了很多方法,用来获取类型相关的各种信息,而 *rtype 实现了 Type 接口。

// src/reflect/type.go 
type Type interface { 
    // 所有的类型都可以调用下面这些函数
    // 此类型的变量内存对齐策略
    Align() int 

    // 如果是 struct 的字段,对齐后占用的字节数
    FieldAlign() int 
    // 返回类型方法集里的第 `i` (传入的参数)个方法
    Method(int) Method 
    // 通过名称获取方法
    MethodByName(string) (Method, bool) 
    // 获取类型方法集里导出的方法个数
    NumMethod() int 
    // 类型名称
    Name() string 
    // 返回类型所在的路径,如:encoding/base64 
    PkgPath() string 
    // 返回类型的大小,和 unsafe.Sizeof 功能类似
    Size() uintptr 
    // 返回类型的字符串表示形式
    String() string 
    // 返回类型的类型值
    Kind() Kind 
    // 类型是否实现了接口 u 
    Implements(u Type) bool 
    // 是否可以赋值给 u 
    AssignableTo(u Type) bool 
    // 是否可以类型转换成 u 
    ConvertibleTo(u Type) bool 
    // 类型是否可以比较
    Comparable() bool 
    // 下面这些函数只有特定类型可以调用
    // 如:Key 和 Elem 两个方法就只能是 Map 类型才能调用

    // 类型所占据的位数
    Bits() int 
    // 返回通道的方向,只能用 chan 类型调用
    ChanDir() ChanDir 
    // 返回类型是否是可变参数,只能用 func 类型调用
    // 比如 t 是类型 func(x int, y ... float64) 
    // 那么 t.IsVariadic() == true 
    IsVariadic() bool 
    // 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用
    Elem() Type 
    // 返回结构体类型的第 i 个字段,只能是结构体类型调用
    // 如果 i 超过了总字段数,就会 panic 
    Field(i int) StructField 
    // 返回嵌套的结构体的字段
    FieldByIndex(index []int) StructField 
    // 通过字段名称获取字段
    FieldByName(name string) (StructField, bool) 
    // FieldByNameFunc returns the struct field with a name 
    // 返回名称符合 func 函数的字段
    FieldByNameFunc(match func(string) bool) (StructField, bool) 
    // 获取函数类型的第 i 个参数的类型
    In(i int) Type 
    // 返回 map 的 key 类型,只能由类型 map 调用
    Key() Type 
    // 返回 Array 的长度,只能由类型 Array 调用
    Len() int 
    // 返回类型字段的数量,只能由类型 Struct 调用
    NumField() int 
    // 返回函数类型的输入参数个数
    NumIn() int 
    // 返回函数类型的返回值个数
    NumOut() int 
    // 返回函数类型的第 i 个值的类型
    Out(i int) Type 
    // 返回类型结构体的相同部分
    common() *rtype 

    // 返回类型结构体的不同部分
    uncommon() *uncommonType 
}

	Type 定义了非常多的方法,通过它们可以获取类型的一切信息。
	注意到 Type 方法集的倒数第二个方法 common 返回的 rtype 类型,它和上一小节讲到的_type 是一回事,而且源码里也有注释强制二者要保持同步:
// src/reflect/type.go 
// rtype must be kept in sync with ../runtime/type.go:/^type._type. 
type rtype struct { 
    size uintptr
    ptrdata uintptr 
    hash uint32 
    tflag tflag 
    align uint8 
    fieldAlign uint8 
    kind uint8 
    equal func(unsafe.Pointer, unsafe.Pointer) bool 
    gcdata *byte 
    str nameOff 
    ptrToThis typeOff 
}
	所有的类型都会包含 rtype 这个字段,表示各种类型的公共信息;另外,不同类型包含自己的
一些独特的部分。
	比如下面的 arrayType 和 chanType 都包含 rytpe,而前者还包含 slice、len 等和数组相关的
信息;后者则包含 dir 表示通道方向的信息。
// src/reflect/type.go 
// arrayType represents a fixed array type. 
type arrayType struct { 
    rtype `reflect:"array"` 
    elem *rtype // array element type 
    slice *rtype // slice type 
    len uintptr 
} 
// chanType represents a channel type. 
type chanType struct { 
    rtype `reflect:"chan"` 
    elem *rtype // channel element type 
    dir uintptr // channel direction (ChanDir) 
}
	注意到,Type 接口实现了 String() 函数,满足 fmt.Stringer 接口,因此使用 fmt.Println 打印
的时候,输出的是 String() 的结果。另外,fmt.Printf() 函数,如果使用 %T 来作为格式参数,输
出的是 reflect.TypeOf 的结果,也就是动态类型。例如:
fmt.Printf("%T", 3) // int

	讲完了 TypeOf 函数,再来看一下 ValueOf 函数。返回值 reflect.Value 表示 interface{} 里存
储的实际变量,它能提供实际变量的各种信息。相关的方法常常需要结合类型信息和值信息。例
如,如果要提取一个结构体的字段信息,那就需要用到 _type (具体到这里是指 structType) 类型持
有的关于结构体的字段信息、偏移信息,以及 *data 所指向的内容,即结构体的实际值。

ValueOf 函数的源码如下:

// src/reflect/value.go 
func ValueOf(i interface{}) Value { 
    if i == nil { 
        return Value{} 
    } 

    // …… 
    return unpackEface(i) 
} 
// 分解 eface 
func unpackEface(i interface{}) Value { 
    e := (*emptyInterface)(unsafe.Pointer(&i)) 
    t := e.type 
    if t == nil { 
        return Value{}
    } 

    f := flag(t.Kind()) 
    if ifaceIndir(t) { 
        f |= flagIndir 
    } 
    return Value{t, e.word, f} 
}
	从源码看,比较简单:先将 i 转换成 *emptyInterface 类型,再将它的 type 字段和 word 字
段以及一个标志位字段组装成一个 Value 结构体,而这就是 ValueOf 函数的返回值,它包含类型
结构体指针、真实数据的地址、标志位。
	Value 结构体定义了很多方法,通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据:
// src/reflect/value.go 
// 设置切片的 len 字段,如果类型不是切片,就会 panic 
func (v Value) SetLen(n int) 

// 设置切片的 cap 字段
func (v Value) SetCap(n int) 

// 设置字典的 kv 
func (v Value) SetMapIndex(key, val Value) 
// 返回切片、字符串、数组的索引 i 处的值
func (v Value) Index(i int) Value 

// 根据名称获取结构体的内部字段值
func (v Value) FieldByName(name string) Value 

// …… 
Value 字段还有很多其他的方法。例如:
// 用来获取 int 类型的值
func (v Value) Int() int64 
// 用来获取结构体字段(成员)数量
func (v Value) NumField() int 
// 尝试向通道发送数据(不会阻塞)
func (v Value) TrySend(x reflect.Value) bool 
// 通过参数列表 in 调用 v 值所代表的函数(或方法)
func (v Value) Call(in []Value) (r []Value) 
// 调用变参长度可变的函数
func (v Value) CallSlice(in []Value) []Value

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值