引言

这个问题第一次出现在看6.824的labrpc时候,其中使用到了reflect.method。当时对反射的理解还只是停留在 reflect.type 和 reflect.value,所以在一些关键的地方读的并不是很懂。去查阅资料的时候惊奇的发现除了官方文档以外竟然没有一篇博客讲这个东西的用法,所以决定在搞懂以后记录一篇博客,以帮助有同样问题的朋友。

正文

reflect.method其实就是用于在函数传入interface{}以后拿到这个接口的函数的定义,然后我们就可以去操作这个reflect.method了。

我们来看一个简单的实现,再去看其定义:

type Person struct {
	Name string
	Age int
	Sex string
}

type tool struct {
	cap string
	key string
}

func (t *tool) print(){
	fmt.Println(t.cap, t.key)
}

func (p Person) Say(msg string){
	fmt.Println("hello," , msg)
}

func (p Person) PrintInfo(t *tool){
	t.cap = "green"
	t.key = "long"
	fmt.Printf("姓名:%s, 年龄:%s, 性别:%s, 参数tool内容:%s %s\n", p.Name, p.Age, p.Sex, t.key, t.cap)
}

type service struct {
	servers map[string]reflect.Method
	rcvr reflect.Value
	typ reflect.Type
}

func MakeService(rep interface{}) (*service) {
	ser := service{}
	ser.typ = reflect.TypeOf(rep)
	ser.rcvr = reflect.ValueOf(rep)
	// name返回其包中的类型名称,举个例子,这里会返回Person,tool
	name := reflect.Indirect(ser.rcvr).Type().Name()
	fmt.Println(name)
	ser.servers = map[string]reflect.Method{}
	fmt.Println( ser.typ.NumMethod(), ser.typ.Name())
	for i:=0 ; i < ser.typ.NumMethod(); i++ {
		method := ser.typ.Method(i)
		mtype := method.Type
		//mtype := method.Type	// reflect.method
		mname := method.Name	// string
		fmt.Println("mname : ", mname)
		ser.servers[mname] = method
	}
	return &ser
}

func main(){
	p1 := Person{"Rbuy", 20, "男"}
	// 得到这个对象的全部方法,string对应reflect.method
	methods := MakeService(p1)
	// 利用得到的methods来调用其值
	methname := "PrintInfo"
	if method, ok := methods.servers[methname]; ok{
		// 得到第一个此method第1参数的Type,第零个当然就是结构体本身了
		replyType := method.Type.In(1)
		replyType = replyType.Elem()	// Elem会返回对
		// New returns a Value representing a pointer to a new zero value for the specified type.
		replyv := reflect.New(replyType)
		function := method.Func
		function.Call([]reflect.Value{methods.rcvr,replyv})
		// 此时我们已经拿到了返回值
	}
}
MakeServicereflect.methodinterface{}
reflect.method
call[]reflect.value
reflect.value
type Method struct {
    // Name is the method name.
    // PkgPath is the package path that qualifies a lower case (unexported)
    // method name.  It is empty for upper case (exported) method names.
    // The combination of PkgPath and Name uniquely identifies a method
    // in a method set.
    // See http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string	
    PkgPath string

    Type  Type  // method type
    Func  Value // func with receiver as first argument
    Index int   // index for Type.Method
}
reflect.methodfunction
Typereflect.valueValue
functionTypeValueMethod

至于它的实际作用,我看到的它的用法就是实现一个本机多协程间的简单RPC,可以通过接口返回传入结构体的全部方法,供其他协程直接调用。至于其他的用处,有待于大家发现啦。