golang反射(三):反射调用对象的方法,调用动态方法

通过反射访问一个对象的方法,要确保此方法是可导出的,否则会报错。

对象类型
type Dog struct {

}

func (t Dog) T1(nums []int) {
	fmt.Println(nums)
	fmt.Println("t1")
}

func (t Dog) T2() {
	fmt.Println("t2")
}
列出对象的方法
var tt Dog

getType := reflect.TypeOf(tt)

for i:= 0; i<getType.NumMethod();i++{
    met := getType.Method(i)
    fmt.Printf("%s, %s, %s, %d\n", met.Type, met.Type.Kind(), met.Name, met.Index)
}

打印

func(main.Dog, []int), func, T1, 0
func(main.Dog), func, T2, 1

不知道你是否理解了这个输出结果,我们定义的对象的方法,比如 T2

func (t Dog) T2() {
	fmt.Println("t2")
}

实际上是这样的

func(main.Dog){
...
}
使用下标调用方法

调用方法使用 Call() 方法,但是搞笑的是这个 Call() 方法居然不是 Method 对象的(也就是上面的 met)而是 Value 对象的。

// Call calls the function v with the input arguments in.
// For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]).
// Call panics if v's Kind is not Func.
// It returns the output results as Values.
// As in Go, each input argument must be assignable to the
// type of the function's corresponding input parameter.
// If v is a variadic function, Call creates the variadic slice parameter
// itself, copying in the corresponding values.
func (v Value) Call(in []Value) []Value {
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("Call", in)
}

在调用的时候会判断 Value 对象的 Kind 是否是 func

好了,我们能否通过 Method 对象得到 Value 对象呢,答案是可以的。

getType := reflect.TypeOf(tt)
met := getType.Method(1)
met.Func // 得到 value of method

查看对象的 Kind

fmt.Println(met.Func.Kind()) // func

既然如此,我就可以调用 Call 方法了,此处以 T2 为例。

met.Func.Call(nil)

好家伙,报错了

panic: reflect: Call with too few input arguments [recovered]
        panic: reflect: Call with too few input arguments

说我少了参数,可是 T2 明明就没有参数啊,改一下 in 的值?。

met.Func.Call([]reflect.Value{})

依然报上面的错误。

上面已经提到了,实际上,T2 的定义是这样的,就是参数的问题

func(main.Dog)

修改后

met.Func.Call([]reflect.Value{reflect.ValueOf(tt)})

果然可以正常调用了。

关于 Call 方法的参数

它的参数是 []Value,也就是 []reflect.Value,是 Value 对象的切片,切片的元素会按先后顺序填入到实参,比如方法有三个参数 arg1, arg2, arg3

in := make([]reflect.Value, 3)
in[0] := reflect.ValueOf(arg1)
in[2] := reflect.ValueOf(arg2)
in[3] := reflect.ValueOf(arg3)

v.Call(in)

如果没有参数,可以传 nil 或者[]reflect.Value{} 或者 make([]reflect.Value, 0)

使用方法名调用方法
getType := reflect.TypeOf(tt)
met, ok := getType.MethodByName("T2")
if !ok {
    panic("method not exist.")
}
met.Func.Call([]reflect.Value{reflect.ValueOf(tt)})
简化调用

上面已经演示了对象方法的调用,但是不符合我们实际的使用,正常的理解是 T2 是不应该传参的。

要想达到这样的效果,我们要使用 reflect.ValueOf()来反射,此处使用 T1 为例。

var tt Dog

getValue := reflect.ValueOf(tt)
m := getValue.Method(0)

// 或者使用方法名称
// m := getValue.MethodByName("T1")

fmt.Println(m.Kind()) // func
fmt.Println(m.Type()) // func([]int)

arg := []int{1,2,3}
m.Call([]reflect.Value{reflect.ValueOf(arg)})

如果有多个参数,参考上面的写法。

为什么这里不需要传入tt 呢,这是 TypeOfValueOf 反射的区别。

但是前面也看到 了,met, ok := getType.MethodByName("T2") 要比 m := getValue.MethodByName("T2")多一个返回值用以判断方法是否存在,这一点很重要,否则就回报错,所以安全的方式为:

if _, ok := getType.MethodByName(scheduleName); ok {
	handler := getValue.MethodByName(scheduleName)
	...
	val := handler.Call([]reflect.Value{reflect.ValueOf(nums)})
}

Call() 的返回值为 []Value,切片中元素的个数就是返回值的个数,下标也一一对应。比如,增加一个 T3 方法

func (t Dog) T3() bool {
	return true
}

调用方法得到返回值

getValue := reflect.ValueOf(tt)
m := getValue.MethodByName("T3")
re := m.Call([]reflect.Value{})
intf := re[0].Interface() // interface{} 类型
b := intf.(bool) // bool 类型
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值