通过反射访问一个对象的方法,要确保此方法是可导出的,否则会报错。
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)})
果然可以正常调用了。
它的参数是 []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
呢,这是 TypeOf
和 ValueOf
反射的区别。
但是前面也看到 了,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 类型