【Golang | reflect】利用反射实现方法的调用-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

引言

go语言中如果某个数据类型实现了一系列的方法如何批量去执行呢这时候就可以利用反射里的func (v Value) Call(in []Value) []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)
}

Call方法实际使用时主要有以下两种调用方式

方法1使用reflect.Type.Method.Func.Call

package main

import (
	"fmt"
	"reflect"
)

type dog struct {
	name string
}

func (a *dog) Speak() {
	fmt.Println("speaking!")
}
func (a *dog) Walk() {
	fmt.Println("walking!")
}

func (a *dog) GetName() (string, error) {
	fmt.Println("Getting name!")
	return a.name, nil
}
func main() {
	var tudou = &dog{name: "tudou"}
	// 获取reflect.Type
	objectType := reflect.TypeOf(tudou)
	// 批量执行方法
	for i := 0; i < objectType.NumMethod(); i++ {
		fmt.Printf("Now method: %v is being executed...\n", objectType.Method(i).Name)
		// Call的第一个入参对应receiver即方法的接受者本身
		ret := objectType.Method(i).Func.Call([]reflect.Value{reflect.ValueOf(tudou)})
		fmt.Printf("The return of method: %s is %v\n\n", objectType.Method(i).Name, ret)
	}
}


1、这里说明下为什么使用Func调用Call时第一个入参是对应receiver本身method.Func.Call([]reflect.Value{reflect.ValueOf(tudou)})
可以看下结构体MethodFunc的定义有这么一句注释func with receiver as first argument

/*
 * The compiler knows the exact layout of all the data structures above.
 * The compiler does not know about the data structures and methods below.
 */

// Method represents a single method.
type Method struct {
	...
	Func  Value // func with receiver as first argument
	...
}

2、objectType.Method(i)返回的是一个Method结构体

方法2使用reflect.Value.Method(index).Call

package main

import (
	"fmt"
	"reflect"
)

type dog struct {
	name string
}

func (a *dog) Speak() {
	fmt.Println("speaking!")
}

func (a *dog) SetName(name string) error {
	fmt.Println("Setting name!")
	a.name = name
	return nil
}

func (a *dog) GetName() (string, error) {
	fmt.Println("Getting name!")
	return a.name, nil
}

func main() {
	var tudou = &dog{name: "tudou"}
	// 获取reflect.Value
	objectValue := reflect.ValueOf(tudou)
	// 根据方法名获取method执行Call
	objectValue.MethodByName("Speak").Call(nil)
	objectValue.MethodByName("SetName").Call([]reflect.Value{reflect.ValueOf("newName")})
	objectValue.MethodByName("GetName").Call(nil)
}


1、不同于方法1使用reflect.Value.Method直接调用Call不需要使用receiver作为第一个入参。可以看下方法MethodByName的注释有这么一句The arguments to a Call on the returned function should not include a receiver

// MethodByName returns a function value corresponding to the method
// of v with the given name.
// The arguments to a Call on the returned function should not include
// a receiver; the returned function will always use v as the receiver.
// It returns the zero Value if no method was found.
func (v Value) MethodByName(name string) Value {
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.MethodByName", Invalid})
	}
	if v.flag&flagMethod != 0 {
		return Value{}
	}
	m, ok := v.typ.MethodByName(name)
	if !ok {
		return Value{}
	}
	return v.Method(m.Index)
}

2、objectValue.MethodByName("Speak")返回的是一个reflect.Value这个跟方法1调用Method()有明显区别

3、另外值得留意的是虽然方法2可以参考方法1的for循环批量执行method但是reflect.Value似乎并没有直接提供方法获取每一个method的Name。但是我们可以根据Index借助reflect.Type.Method(Index).Name来获取Name这是因为每一个method的NameIndex是一一对应的

type Method struct {
	// Name is the method name.
	Name string
	...
	Index int   // index for Type.Method
}

其实注1里的方法func (v Value) MethodByName(name string) Value 里有一段也是根据这个对应关系实现的m, ok := v.typ.MethodByName(name) ... return v.Method(m.Index)有兴趣的同学可以留意观察下

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go

“【Golang | reflect】利用反射实现方法的调用-CSDN博客” 的相关文章