引言
这个问题第一次出现在看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})
// 此时我们已经拿到了返回值
}
}
MakeService
做的事情是通过interface{}拿到其全部的reflect.method
,然后返回一个这个interface{}
的成员名对应method的映射,这样我们就可以在其他地方去调用这个函数了,而且我们只需要传入一个入参就可以调用了。
看完这个代码基本就知道reflect.method
的作用了。我们不妨想一想反射的作用是什么?Go语言圣经对于反射的定义是这样的:在编译时不知道类型的情况下,可更新变量,在运行时查看值,调用方法以及直接对它们的布局进行操作。当然这是基于interface来说的。通俗来说,反射可以让我们在一个interface{}中直接拿到其动态值和动态类型,同样可以修改这个动态值其中的成员和调用其函数,那么value可以让我们调用值,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.method
其实就是function
变量的一个封装罢了,也可以知道Value能用的方法method当然也可以使用了。
只不过基于这个变量我们能做的事情就有很多了,比如我们通过Type
可以拿出方法入参的任意个参数类型,从而构建一个此类型的reflect.value
。我们还可以直接调用这个method,当然是通过Value
来做这个事情。
所以简而言之,一个function
的Type
和Value
能做的事情Method
都可以做,这就是它的作用。
至于它的实际作用,我看到的它的用法就是实现一个本机多协程间的简单RPC,可以通过接口返回传入结构体的全部方法,供其他协程直接调用。至于其他的用处,有待于大家发现啦。