go 反射
Tip
建议先看下接口章节
1 元数据
什么是元数据?
是描述数据的数据
比如一个结构体对象是个数据, 它是什么类型,这个类型就是用来描述这个它自己的一种数据
这个结构体对象的值,比如 Dog{Name:“tom”} tom 这种就是它的值, 它显然也是描述该结构体的一种数据
我们只要有了一个数据的类型和值, 那么我们就知道了该数据的全部信息
2 三定律
2.1 接口类型变量转换为反射对象
2.1.1 类型 reflect.Type
Tip
- 是接口,用于获取元数据 : “类型”信息
- reflect.TypeOf() 对象转换为Type接口的方法
重要
- 从源码看, 刚好和我们之前讲到的接口与元数据将变量转为接口类型,就可以获取它的元数据的推断一致
- 后面比如
.Field(0)这类操作的底层与 接口章节的代码验证结构 原理一致
package test
import (
"fmt"
"reflect"
"testing"
)
type Dog struct {
Age int
name string
}
func (d Dog) shout() {
println("wang ...")
}
func (d Dog) Show() {
println("show ...")
}
func (d *Dog) SetName(name string) {
d.name = name
}func TestA(t *testing.T) {
d := Dog{11, "tom"}
typ := reflect.TypeOf(d)
fmt.Println(typ.Name(), typ.Kind())
if typ.Kind() == reflect.Struct {
// 字段
numField := typ.NumField()
for i := 0; i < numField; i++ {
// 可以获取 private 字段 信息
fmt.Println(typ.Field(i).Name, "是否公开:", typ.Field(i).IsExported())
}
// 方法
numMethod := typ.NumMethod()
fmt.Println("方法个数:", numMethod) //1
for i := 0; i < numMethod; i++ {
// 只能获取public 方法 ,Show
fmt.Println(typ.Method(i).Name)
}
}
}func TestA(t *testing.T) {
ptrD := &Dog{11, "tom"}
ptrTyp := reflect.TypeOf(ptrD)
// kind 是ptr指针
fmt.Println(ptrTyp.Name(), ptrTyp.Kind())
// 方法
numMethod := ptrTyp.NumMethod()
fmt.Println("方法个数:", numMethod) // 2
for i := 0; i < numMethod; i++ {
// SetName Show (前面说过编译器会默认生成一个对应的 *Dog 为接受者的方法)
fmt.Println(ptrTyp.Method(i).Name)
}
fmt.Println(ptrTyp.Elem().Kind()) // struct
}2.1.2 值 reflect.Value
Tip
- 是结构体,用于获取元数据: “值”信息
- reflect.ValueOf() 对象转换为Value结构体的方法
package test
import (
"fmt"
"reflect"
"testing"
)
type Dog struct {
Age int
name string
}
func (d Dog) shout() {
println("wang ...")
}
func (d Dog) Show() {
println("show ...")
}
func (d *Dog) SetName(name string) {
d.name = name
}func TestA(t *testing.T) {
d := Dog{11, "tom"}
val := reflect.ValueOf(d)
typFromVal := val.Type()
numField := val.NumField()
for i := 0; i < numField; i++ {
field := val.Field(i)
typField := typFromVal.Field(i)
if typField.IsExported() {
// 私有的字段无法 获取它的值的
// 值无法修改 ,因为 方法是值转递,无意义,指针的话就可以了
fmt.Println(field.Interface(), field.CanSet())
} else {
fmt.Println(typField.Name)
}
}
}func TestA(t *testing.T) {
ptrD := &Dog{11, "tom"}
ptrVal := reflect.ValueOf(ptrD)
val = ptrVal.Elem()
typFromVal = val.Type()
numField = val.NumField()
for i := 0; i < numField; i++ {
field := val.Field(i)
typField := typFromVal.Field(i)
if typField.IsExported() {
fmt.Println(field.Interface(), field.CanSet())
field.SetInt(3)
fmt.Println(ptrD.Age)
// 或
field.Set(reflect.ValueOf(5))
fmt.Println(ptrD.Age)
} else {
fmt.Println(typField.Name)
}
}
fmt.Println(ptrD.Age)
}2.2 反射对象转换为接口类型变量
reflect.Interface()
转为接口类型的变量, 然后可通过类型断言等转为原来的类型
2.3 可变性
3 基础操作
type Dog struct {
Age int
Name string
}
func (d Dog) SetName(name string) {
d.Name = name
}
func TestReflect(t *testing.T) {
d := &Dog{Age: 2, Name: "ff"}
dT := reflect.TypeOf(d)
for i := 0; i < dT.NumMethod(); i++ {
// 打印方法
fmt.Println(dT.Method(i).Name, "==", dT.Method(i).Type, "==", dT.Method(i).Type.Kind())
}
vv := reflect.ValueOf(d)
args := []reflect.Value{reflect.ValueOf("tom")}
// 调用方法
vv.MethodByName(dT.Method(0).Name).Call(args)
fmt.Println(d.Name)
}