这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天,记录对 go 语言反射的学习。
什么是反射
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
Go 语言的反射可以让我们在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作(但是在编译时并不知道这些变量的具体类型),也可以让我们将类型本身作为第一类的值类型处理。
Go 语言的反射功能由 reflect 包提供,它定义了两个重要的类型 Type 和 Value,并且提供了 reflect.TypeOf() 和 reflect.ValueOf() 两个函数来获取任意对象的 Value 和 Type。
获取类型
可以使用 refect.TypeOf() 函数来获取任意对象的类型,该类型对象的类型为 reflect.Type。
使用 reflect.Type 中的 Name() 方法可以获得类型的名称(该类型必须显示定义,否则会得到空字符串),使用 reflect.Type 中的 Kind() 方法可以获得一个类型为 reflect.Kind 的枚举值(结构体总是返回 reflect.Struct,指针总是返回 reflect.Pointer)。
package main
import (
"fmt"
"reflect"
)
type A struct {}
func main() {
i := 1
typeOf := reflect.TypeOf(i)
fmt.Println(typeOf, typeOf.Name(), typeOf.Kind())
a := A{}
typeOf = reflect.TypeOf(a)
fmt.Println(typeOf, typeOf.Name(), typeOf.Kind())
typeOf = reflect.TypeOf(&a)
fmt.Println(typeOf, typeOf.Name(), typeOf.Kind())
}
// 输出
int int int
main.A A struct
*main.A ptr
获取值
可以使用 refect.ValueOf() 函数来获取任意对象的值,该值对象的类型为 reflect.Value。
package main
import (
"fmt"
"reflect"
)
func main() {
i := 100
valueOf := reflect.ValueOf(i)
var c int = int(valueOf.Int())
fmt.Println(c)
}
// 输出
100
获取结构体字段信息
使用 reflect.Type 的 Field() 或者 FieldByName() 方法可以获取结构体字段的信息,类型为 reflect.StructField。
type StructField struct {
Name string // 字段名
PkgPath string // 字段路径
Type Type // 字段反射类型对象
Tag StructTag // 字段的结构体标签
Offset uintptr // 字段在结构体中的相对偏移
Index []int // Type.FieldByIndex中的返回的索引值
Anonymous bool // 是否为匿名字段
}
使用reflect.Value 的 Field() 或者 FieldByName() 方法可以获取结构体字段的值。
获取结构体标签
结构体标签规范:由一个或多个键值对组成,键与值使用冒号分隔,值要加上双引号,键值对之间使用空格分隔。
`key1:"value2" key2:"value2"`
可以使用 reflect.Tag.Get() 方法来获取 tag 对应 key 的值。
package main
import (
"fmt"
"reflect"
)
type User struct {
Id string `json:"id"`
Name string `json:"name"`
}
func main() {
typeOf := reflect.TypeOf(User{})
name, _ := typeOf.FieldByName("Name")
fmt.Println(name.Tag.Get("json"))
}
// 输出
name