首先贴上 Go 开发团队对 reflect 包的描述:
Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.
A call to ValueOf returns a Value representing the run-time data. Zero takes a Type and returns a Value representing a zero value for that type.
从描述中,我们得到以下几点:
- reflect 包实现了运行时的反射机制,允许程序操作任意类型的对象;
- TypeOf 可以得到一个 interface{} 的具体类型,ValueOf 可以得到一个 interface{} 的具体值;
重要类型
reflect 包中定义了几种重要的、常用的类型,分别为:
- Kind;
- Value;
- SliceHeader;
- StringHeader;
- Method;
- StructField;
Kind
Kind 类型用于修饰类型 Type
,用于表示 Type
的种类,底层是用一个无符号整数表示:
type Kind uint;
其中修饰了的基础数据类型包括Bool
、Int
、Int32
、Float32
、Float64
等,引用数据类型包括Slice
、Map
、Chan
、Interface
、Func
等。
package main
import (
"fmt"
"reflect"
)
func main() {
var v1 int = 1
var v2 float32 = 1.0
v3 := struct {
}{}
v4 := func() {}
PrintKind(v1)
PrintKind(v2)
PrintKind(v3)
PrintKind(v4)
}
func PrintKind(v interface{}) {
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Int:
fmt.Println("int")
case reflect.Float32:
fmt.Println("float32")
case reflect.Struct:
fmt.Println("struct")
case reflect.Func:
fmt.Println("function")
// 其它的情况 ...
}
}
int
float32
struct
function
如果只是单纯地打印类型 Type
的种类,直接调用 .String
方法即可。
func PrintKind(v interface{}) {
t := reflect.TypeOf(v)
fmt.Println(t.String())
}
int
float32
struct {}
func()
Value
Value 类型表示一个接口具体的值,类型的定义如下:
type Value struct {
// 值的真实类型
typ *rtype
// 指向值的指针
ptr unsafe.Pointer
// 值的元数据信息
// flag 可以是多种情况的组合
flag
}
在知道值类型的情况下,可以调用相应的函数获取其本身的值,即值的类型是 int
,经过 ValueOf
返回 Value
类型的值就可以调用 Int
方法得到其值。但是,如果调用了不符合本身类型的方法会报错,这一类方法的开头都对值的类型做了类型判断。
package main
import (
"fmt"
"reflect"
)
func main() {
var v1 int = 1
fmt.Println(reflect.ValueOf(v1).Int())
var v2 float32 = 1.0
// fmt.Println(reflect.ValueOf(v2).Int()) 会报错
}
func (v Value) Int() int64 {
k := v.kind()
p := v.ptr
switch k {
case Int:
return int64(*(*int)(p))
case Int8:
return int64(*(*int8)(p))
case Int16:
return int64(*(*int16)(p))
case Int32:
return int64(*(*int32)(p))
case Int64:
return *(*int64)(p)
}
panic(&ValueError{"reflect.Value.Int", v.kind()})
}
func (f flag) mustBe(expected Kind) {
// TODO(mvdan): use f.kind() again once mid-stack inlining gets better
if Kind(f&flagKindMask) != expected {
panic(&ValueError{methodName(), f.kind()})
}
}
SliceHeader
SliceHeader 是 slice 运行时的底层实现,其结构如下:
type SliceHeader struct {
// 指向底层数据的指针
// 无符号的整数表示内存中的地址
Data uintptr
// slice 的长度
Len int
// slice 的容量
Cap int
}
func main() {
data := []int{1, 2, 3, 4, 5}
// unsafe.Pointer 表示任意类型的指针
dataPointer := unsafe.Pointer(&data)
sh1 := (*reflect.SliceHeader)(dataPointer)
fmt.Printf(`SliceHeader {
Data: %d
Len: %d
Cap: %d
}
`, sh1.Data, sh1.Len, sh1.Cap)
for i := 0; i < sh1.Len; i++ {
// slice 中各元素的地址
addr := uint(sh1.Data) + uint(unsafe.Sizeof(1)) * uint(i)
// slice 中各元素的值
value := *(*int)(unsafe.Pointer(uintptr(addr)))
fmt.Printf("%dth element addr: 0x%x, value: %d\n", i, addr, value)
}
}
SliceHeader {
Data: 824634670792
Len: 5
Cap: 5
}
0th element addr: 0xc0000e7ec8, value: 1
1th element addr: 0xc0000e7ed0, value: 2
2th element addr: 0xc0000e7ed8, value: 3
3th element addr: 0xc0000e7ee0, value: 4
4th element addr: 0xc0000e7ee8, value: 5
上述代码构造了一个 SliceHeader 类型的变量 sh1
,并逐一打印出各元素的地址和值:
&data
得到了 slice 的指针,使用unsafe.Pointer(&data)
强制转换变成*ArbitraryType
;- 使用
(*reflect.SliceHeader)(dataPointer)
得到了一个指向 SliceHeader 的指针; fmt.Printf
打印变量中 3 个字段的信息;- for 循环:先计算每一个元素的地址,再通过地址得到地址上的值,最后打印输出;
- 地址和值都需要进行一系列转换;
- 如 uintptr 不支持算法运算符,所以通过转换成 uint 类型进行计算;
- 值的获取则是先将 uint 转换成 uintptr,再通过
unsafe.Pointer
转换成可表示任意类型的指针,接着转成*int
类型的指针,最后使用*
取指针的值;
StringHeader
StringHeader 是 string 运行时的底层实现,其结构如下:
type StringHeader struct {
// 指向底层数据的指针
// 无符号的整数表示内存中的地址
Data uintptr
// 字符串的长度
Len int
}
func main() {
data := []string{"1", "22", "333", "4444", "55555"}
sh1 := (*reflect.StringHeader)(unsafe.Pointer(&data))
fmt.Printf("string slice length: %d\n", sh1.Len)
for i := 0; i < sh1.Len; i++ {
// 计算第 i 个元素的地址
addr := uint(sh1.Data) + uint(i) * uint(unsafe.Sizeof(data[i]))
// 转换成任意类型的指针
arbitraryPointer := unsafe.Pointer(uintptr(addr))
// 转换成字符串指针
stringPointer := (*string)(arbitraryPointer)
value := *stringPointer
fmt.Printf("%dth value: %s\n", i, value)
}
}
string slice length: 5
0th value: 1
1th value: 22
2th value: 333
3th value: 4444
4th value: 55555
StringHeader 的例子与 SliceHeader 类似,区别在于在指针转换时 StringHeader 使用了 *string
。
Method
Method 表示一个方法,可以使用 reflect 包动态的调用方法,传入的参数是一个 reflect.Value
的 slice,其返回值也是使用一个 reflect.Value
的 slice 表示。
type anyFunc func(int) int
type A struct {}
func (a A) AnyFunc() string { return "A" }
func main() {
var oneFunc anyFunc = func(i int) int {
return i + 1
}
// oneFunc 是一个函数指针
fmt.Printf("1. pointer %p\n", oneFunc)
// 函数类型的字符串表示
fmt.Printf("2. %s\n", reflect.TypeOf(oneFunc).String())
// 函数指针
fmt.Printf("3. %s\n", (reflect.ValueOf(&oneFunc)).Kind().String())
fmt.Println("== call method by reflect package")
i := 1000
// reflect.Value 得到函数值
// .Call 调用函数
// []reflect.Value{reflect.ValueOf(i)} 是传入函数的参数
result := reflect.ValueOf(oneFunc).Call([]reflect.Value{reflect.ValueOf(i)})
fmt.Println(result[0])
// 类型中的方法
a := A{}
method := reflect.TypeOf(a).Method(0)
fmt.Printf(`== basic information of A.AnyFunc
Type: %s
Name: %s
PkgPath: %s
Index: %d
`, method.Type, method.Name, method.PkgPath, method.Index)
// 调用类型中的方法
fmt.Println("== call a.AnyFunc")
fmt.Println(reflect.ValueOf(a).Method(0).Call([]reflect.Value{}))
}
StructField
StructField 表示一个结构的字段,StructField 还会保存字段的 Tag 信息(StructTag)。StructTag 的底层实现是 string:
type StructTag string
type A struct {
StringField string `json:"jsonStringField"`
IntField int `json:"jsonIntField"`
}
func main() {
a := A{
StringField: "simple string",
IntField: 100,
}
fmt.Printf("number of struct fields: %d\n", reflect.TypeOf(a).NumField())
fmt.Println("== get struct field value")
numField := reflect.TypeOf(a).NumField()
for i := 0; i < numField; i++ {
fmt.Printf("%s: %v\n", reflect.TypeOf(a).Field(i).Name, reflect.ValueOf(a).Field(i))
}
fmt.Println("== get struct field tag")
for i := 0; i < numField; i++ {
tag := reflect.TypeOf(a).Field(i).Tag
fmt.Printf("%s's tag string: `%s`\n", reflect.TypeOf(a).Field(i).Name, tag)
fmt.Printf(" json: %v\n", tag.Get("json"))
}
}
总结
- reflect.ValueOf(值) 和 reflect.ValueOf(指针).Elem() 等价;
- unsafe.Pointer 提供了任意类型指针的访问;
评论