本文主要是介绍Go反射四讲---第三讲:如何使用反射操作函数,获取函数的相关信息?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
反射-函数
这是我们反射四讲的第三讲,本次给大家讲解如何使用反射处理函数相关的操作。
在这一部分,向大家展示如何输出方法的信息并执行调用。
输出信息,包含方法名,方法参数,返回值。
最后,如何使用反射调用函数。
提醒:在实现时候,我们要非常注意指针的使用,不然会出现 Bug。
测试函数
type User struct {Name stringAge 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 stringinput anywantRes map[string]*FuncInfowantErr 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)})}
}
总结
对于方法接收器:
- 以结构体作为输入,那么只能访问到结构体作为接收器的方法
- 以指针作为输入,那么能访问到任何接收器的方法
输入的第一个参数,永远都是接收器本身。
这篇关于Go反射四讲---第三讲:如何使用反射操作函数,获取函数的相关信息?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!