这个问题第一次出现在看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,可以通过接口返回传入结构体的全部方法,供其他协程直接调用。至于其他的用处,有待于大家发现啦。