反射-函数
这是我们反射四讲的第三讲,本次给大家讲解如何使用反射处理函数相关的操作。
在这一部分,向大家展示如何输出方法的信息并执行调用。
输出信息,包含方法名,方法参数,返回值。
最后,如何使用反射调用函数。
提醒:在实现时候,我们要非常注意指针的使用,不然会出现 Bug。
测试函数
type User struct {
Name string
Age int
}
func NewUserPoint(name string, age int) *User {
return &User{
Name: name,
Age: age,
}
}
func NewUser(name string, age int) User {
return User{
Name: name,
Age: age,
}
}
func (u User) GetAge() int {
return u.Age
}
func (u *User) ChangeName(NewName string) {
u.Name = NewName
}
func (u *User) private() {
fmt.Println("private")
}
定义元数据
type FuncInfo struct {
Name string // 方法名
In []reflect.Type // 方法输入参数
Out []reflect.Type // 方法输出参数
Result []any
}
使用调用
// CallMethod 输出方法的信息并执行调用, 输出:方法名,方法参数,返回值
func CallMethod(val any) (map[string]*FuncInfo, error) {
typ := reflect.TypeOf(val)
if typ.Kind() != reflect.Struct && typ.Kind() != reflect.Ptr {
return nil, errors.New("非法类型")
}
// 构建结果集
num := typ.NumMethod()
result := make(map[string]*FuncInfo, num)
for i := 0; i < num; i++ {
fc := typ.Method(i)
// 输入
numIn := fc.Type.NumIn()
ps := make([]reflect.Value, 0, fc.Type.NumIn())
// 第一个参数永远是接收者,
ps = append(ps, reflect.ValueOf(val))
in := make([]reflect.Type, 0, fc.Type.NumIn())
for j := 0; j < numIn; j++ {
p := fc.Type.In(j)
in = append(in, p)
if j > 0 {
ps = append(ps, reflect.Zero(p))
}
}
// 调用方法
ret := fc.Func.Call(ps)
// 输出
outNum := fc.Type.NumOut()
out := make([]reflect.Type, 0, outNum)
res := make([]any, 0, outNum)
for k := 0; k < outNum; k++ {
out = append(out, fc.Type.Out(k))
res = append(res, ret[k].Interface())
}
result[fc.Name] = &FuncInfo{
Name: fc.Name,
In: in,
Out: out,
Result: res,
}
}
return result, nil
}
测试:
func TestCallMethod(t *testing.T) {
testcases := []struct {
name string
input any
wantRes map[string]*FuncInfo
wantErr error
}{
{
name: "normal struct",
input: types.User{},
wantRes: map[string]*FuncInfo{
"GetAge": {
Name: "GetAge",
In: []reflect.Type{reflect.TypeOf(types.User{})},
Out: []reflect.Type{reflect.TypeOf(0)},
Result: []any{0},
},
},
},
{
// 指针
name: "pointer",
input: &types.User{},
wantRes: map[string]*FuncInfo{
"GetAge": {
Name: "GetAge",
In: []reflect.Type{reflect.TypeOf(&types.User{})},
Out: []reflect.Type{reflect.TypeOf(0)},
Result: []any{0},
},
"ChangeName": {
Name: "ChangeName",
In: []reflect.Type{reflect.TypeOf(&types.User{}), reflect.TypeOf("")},
Out: []reflect.Type{},
Result: []any{},
},
},
},
}
for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
res, err := CallMethod(tt.input)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
assert.Equal(t, tt.wantRes, res)
})
}
}
总结
对于方法接收器:
- 以结构体作为输入,那么只能访问到结构体作为接收器的方法
- 以指针作为输入,那么能访问到任何接收器的方法
输入的第一个参数,永远都是接收器本身。