Golang利用反射reflect动态调用方法

栏目: Go · 发布时间: 7年前

内容简介:Golang的官方包 reflect 实现了运行时反射(run-time reflection)。运用得当,可谓威力无穷。今天,我们就来利用reflect进行方法的动态调用……首先,反射主要与 golang 的 interface 类型相关。一个 interface 类型的变量包含了两个指针:一个指向变量的类型,另一个指向变量的值。最常用的莫过于这两个函数:其中,

Golang的官方包 reflect 实现了运行时反射(run-time reflection)。运用得当,可谓威力无穷。今天,我们就来利用reflect进行方法的动态调用……

基本知识

首先,反射主要与 golang 的 interface 类型相关。一个 interface 类型的变量包含了两个指针:一个指向变量的类型,另一个指向变量的值。最常用的莫过于这两个函数:

func main(){
	s := "hello world"
	fmt.Println(reflect.ValueOf(s))   // hello world
	fmt.Println(reflect.TypeOf(s))    // string
}

其中,

reflect.ValueOf() 返回值类型:reflect.Value
reflect.TypeOf() 返回值类型:reflect.Type

创建变量

接下来,我们可以使用 reflect  来动态的创建变量:

func main(){
	var s string
	t := reflect.TypeOf(s)
	fmt.Println(t)                 // string
	sptr := reflect.New(t)
	fmt.Printf("%s\n", sptr)       // %!s(*string=0xc00000e1e0)
}

需要留意, reflect.New() 返回的是一个 指针

New returns a Value representing a pointer to a new zero value for the specified type. That is, the returned Value’s Type is PtrTo(typ).

这时候,我们可以使用 reflect.Value.Elem() 来取得其实际的值:

sval := sptr.Elem()    // 返回值类型:reflect.Value

然后再将其转为 interface 并做 type-assertion

ss := sval.interface().(string)
fmt.Println(ss)       // 空字符串

动态调用

假设我们已经定义了以下的 struct 并实现了相关的方法:

type M struct{}
type In struct{}
type Out struct{}
 
func (m *M) Example(in In) Out {
	return Out{}
}

然后我们就可以通过下面这种方式来进行调用了:

func main() {
	v := reflect.ValueOf(&M{})
	m := v.MethodByName("Example")
	in := m.Type().In(0)
	out := m.Type().Out(0)
	fmt.Println(in, out)
       
	inVal := reflect.New(in).Elem()
        // 可以将 inVal 转为interface后进行赋值之类的操作……
	rtn := m.Call([]reflect.Value{inVal})
	fmt.Println(rtn[0])
}

注册方法

我们再定义一个保存 M 所有方法的 map struct

type Handler struct {
	Func   reflect.Value
	In     reflect.Type
	NumIn  int
	Out    reflect.Type
	NumOut int
}

然后我们就可以来遍历结构体 M 的所有方法了:

func main() {
	handlers := make(map[string]*Handler)
	v := reflect.ValueOf(&M{})
	t := reflect.TypeOf(&M{})
	for i := 0; i < v.NumMethod(); i++ {
		name := t.Method(i).Name
		// 可以根据 i 来获取实例的方法,也可以用 v.MethodByName(name) 获取 
		m := v.Method(i)
		// 这个例子我们只获取第一个输入参数和第一个返回参数
		in := m.Type().In(0)
		out := m.Type().Out(0)
		handlers[name] = &Handler{
			Func:   m,
			In:     in,
			NumIn:  m.Type().NumIn(),
			Out:    out,
			NumOut: m.Type().NumOut(),
		}
	}
}

Elem()

在学习 reflect 的过程中,我们发现 reflect.Valuereflect.Type 都提供了 Elem() 方法。

reflect.Value.Elem() 的作用已经在前面稍微提到了,主要就是返回一个 interface 或者 pointer 的值:

Elem returns the value that the interface v contains or that the pointer v points to. It panics if v’s Kind is not Interface or Ptr. It returns the zero Value if v is nil.

reflect.Type.Elem() 的作用则是返回一个类型(如:Array,Map,Chan等)的元素的类型:

Elem returns a type’s element type. It panics if the type’s Kind is not Array, Chan, Map, Ptr, or Slice.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Elements of Programming

Elements of Programming

Alexander A. Stepanov、Paul McJones / Addison-Wesley Professional / 2009-6-19 / USD 39.99

Elements of Programming provides a different understanding of programming than is presented elsewhere. Its major premise is that practical programming, like other areas of science and engineering, mus......一起来看看 《Elements of Programming》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具