反射
反射概念
在编译时不知道类型的情况下,可更新变量、在运行时查看值、调用方法以及直接对他们的布局进行操作。
Type
获取变量的类型
go
func testReflect() {
type1 := reflect.TypeOf(1)
type2 := reflect.TypeOf("hello world")
type3 := reflect.TypeOf(Person{}) // 非指针类型
type4 := reflect.TypeOf(&Person{}) // 指针类型
fmt.Printf("type of `%d` is %s\n", 1, type1) // int
fmt.Printf("type of `%s` is %s\n", "hello world", type2) // string
fmt.Printf("type of `%s` is %s\n", "Person{}", type3) // Person
fmt.Printf("type of `%s` is %s\n", "&Person{}", type4) // *Person
fmt.Printf("获取指针指向的底层值类型 -> type4.Elem().equals(type3): %#v\n", type4.Elem() == type3)
}
reflect.TypeOf()获取变量的类型Elem():用于获取反射值的元素类型,用于指针的类型时,会返回指针指向的底层值的类型;用于非引用类型,会导致 panic
获取结构体成员变量
go
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"addr"`
}
func (p Person) GetName() string {
return p.Name
}
func (p *Person) GetName2(in Person) string {
return in.Name
}
func testReflect() {
p := reflect.TypeOf(Person{Name: "jack", Age: 48, Address: "地球村"}) // 不能用指针
numField := p.NumField() // 获取变量数量
for i := 0; i < numField; i++ {
field := p.Field(i)
fmt.Printf("字段名称:%s, 字段内存偏移量:%d, 是否为匿名成员:%t, 数据类型:%v, 包外是否可见:%t, 字段 tag: %s\n",
field.Name, field.Offset, field.Anonymous, field.Type, field.IsExported(), field.Tag.Get("json"))
}
// 通过字段名获取字段
if name, ok := p.FieldByName("Name"); ok {
fmt.Printf("Name is exported: %t\n", name.IsExported())
}
}获取结构体的成员方法
go
func testReflect() {
p2 := reflect.TypeOf(&Person{})
for i := 0; i < p2.NumMethod(); i++ {
method2 := p2.Method(i)
fmt.Printf("方法名:%s, 方法类型:%s, 包外是否可见:%t\n", method2.Name, method2.Type, method2.IsExported())
}
}
reflect.TypeOf()传入指针类型,那么返回了指针指向的类型的所有方法(包括值接收者方法和指针接收者方法);如果传入值类型那么只会包含该类型的值接收者方法(设计如此)- 方法名必须是 exported,否则反射无法获取到
获取函数信息
go
func GetName3(in Person) string {
return in.Name
}
func testReflect() {
typeFunc := reflect.TypeOf(GetName3)
var in, out []string
for i := 0; i < typeFunc.NumIn(); i++ {
in = append(in, typeFunc.In(i).String())
}
for i := 0; i < typeFunc.NumOut(); i++ {
out = append(out, typeFunc.Out(i).String())
}
fmt.Printf("函数名称:%s, 函数入参:%s, 函数出参:%s\n", typeFunc.Name(), strings.Join(in, ","), strings.Join(out, ","))
}判断是否实现接口
go
type People interface {
GetName() string
}
func (p Person) GetName() string {
return p.Name
}
func testReflect() {
typePeople := reflect.TypeOf((*People)(nil)).Elem() // People 是接口无法创建,需要通过 nil 强制转换
p1 := reflect.TypeOf(Person{})
fmt.Printf("People 是否是接口:%t, Person 是否实现接口 People:%t\n",
typePeople.Kind() == reflect.Interface, p1.Implements(typePeople))
}Value
基本操作
go
func main() {
fmt.Printf("-------------------------> reflect.ValueOf() <-------------------------\n")
value := reflect.ValueOf(1)
fmt.Println(value)
t := value.Type() // value 转 type
fmt.Printf("value: %#v, type: %s, kind: %s, cast value: %#v",
value, t, t.Kind(), value.Interface().(int)) // 强制转换数据
person := &Person{Name: "aio", Address: "china", Age: 18}
pv := reflect.ValueOf(person) // 指针 value
pv2 := pv.Elem() // 指针转为非指针 value
pv2.Addr() // 非指针转为指针 value
p3 := pv.Interface().(*Person).Name // 获取初始值
fmt.Println(p3)
var p4 interface{}
v := reflect.ValueOf(p4) // 没有指向具体的值
var p5 *Person = nil
v1 := reflect.ValueOf(p5) // 获取 nil 值
fmt.Printf("v invalid? %t, v1 is nil? %t\n", v.Kind() == reflect.Invalid, v1.IsNil())
}修改变量
go
func main() {
p6 := &Person{
Name: "jack",
Age: 18,
Address: "china",
}
v6 := reflect.ValueOf(p6) // 必须传递指针才能修改原来的值
if ok := v6.Elem().FieldByName("Name").CanSet(); ok { // CanSet() 用来判断成员是否导出,只有可以导出的成员变量才能被修改
v6.Elem().FieldByName("Name").SetString("lucy") // 必须将指针转为非指针后才能修改值
}
fmt.Printf("new value: %#v\n", v6)
v7 := reflect.ValueOf(make([]int, 1, 5)) // 修改 slice 的值
if v7.Len() > 0 {
v7.Index(0).SetInt(10)
}
fmt.Printf("v7 value: %#v\n", v7)
}调用函数
go
func main() {
fmt.Printf("-------------------------> reflect.ValueOf() 调用函数 <-------------------------\n")
valueFunc := reflect.ValueOf(GetName3)
slice1 := make([]reflect.Value, 1)
slice1[0] = reflect.ValueOf(Person{Name: "zhangsan", Age: 18, Address: "usa"})
resultValue := valueFunc.Call(slice1)
fmt.Printf("resultValue: %#v\n", resultValue[0].String())
}调用成员方法
go
func main() {
fmt.Printf("-------------------------> reflect.ValueOf() 调用成员方法 <-------------------------\n")
v8 := reflect.ValueOf(&Person{
Name: "jack",
Age: 18,
Address: "China",
})
method := v8.MethodByName("GetName")
resultValue1 := method.Call([]reflect.Value{})
fmt.Printf("resultValue: %#v\n", resultValue1[0].String())
}
🎈🎈