golang 通过reflect反射方式调用对象方法 (动态方法调用) 详解

在go语言中有一个reflect反射包是非常强大的一个包, 通过反射我们几乎可以对对象干任何事情, 今天就给大家讲一下通过反射动态调用对象方法 的方法。 “ 对象方法 的方法” 这里怎么会有2个“方法” 是不是有点绕?   非也,这里的第一个“方法” 指的是go语言中我们给对象绑定的“函数” ,在go语言里面我们称呼他为 方法!  其实他和函数的定义基本是一样的,只是在函数名称前面多了一个对象绑定定义而已。 方法只属于某个对象,只能通过这个对象调用, 而函数则是任何人都可以调用。 这里因为很多小伙伴对go语言里面的方法和函数 搞不清楚,故多说了几句。

在go语言中 通过反射调用方法有2种途径,

1. 通过反射对象的类型值,即 reflect.TypeOf

  通过对象类型,然后可以通过 .NumMethod() 获取对象拥有方法的个数,然后再通过rt.Method(i) 来获取具体的对方方法对象,然后调用方法的 Func.Call动态来调用对象中的方法。

以下是实现示例:

// 反射动态调用对象方法 示例
// @author: tekintian <tekintian@gmail.com>
// @see https://dev.tekin.cn
func RefDynCall(data interface{}) {
	
	rt:=reflect.TypeOf(data)
	mnums:=rt.NumMethod() // 首先获取对象的方法数量
	// 循环调用所有方法
	for i := 0; i < mnums; i++ {
		rm:=rt.Method(i)
		//  动态调用方法  Call方法后面跟的是Value切片类型的参数,
		// 如果调用的方法没有参数, 则这里需要传递一个空对象 即  []reflect.Value{}  注意不能传nil
		// 如果有参数,可这样传递 如: []reflect.Value{reflect.ValueOf(123)}
		rfvs:=rm.Func.Call([]reflect.Value{})
		// 这里的函数调用返回结果rfvs,是一个切片 []reflect.Value
		// 如果需要返回对应的值,可以根据reflect.Value的类型来调用相关的方法返回具体的值
		// 注意, reflect.Value 如果是一个指针类型的值,则必须调用 .Elem() 方法获取指针对应的值后再调用相关的类型返回方法
		rfvs[0].Interface() // 返回接口类型,  string类型 rfvs[0].String()
		log.Printf("第一个返回值 %v \n",rfvs[0].Interface())

		// // 带参数的调用 这个套路都差不多 下面是一些获取参数的个数, 名称类型的示例
		// rtm:=rm.Type // 获取方法对应的参数类型
		// rtn:= rtm.NumField() // 这个是获取参数的个数
		// rvParams:=make([]reflect.Value,rtn) // 这个用来接收参数
		// for i := 0; i < rtn; i++ {
		// 	rtf1:=rtm.Field(0) // 这个就是第一个参数的对象 reflect.StructField
		// 	//rtf1.Name //第一个参数的名字
		// 	rtf1tk:=rtf1.Type.Kind()  //第一个参数的类型
		// 	switch rtf1tk {
		// 	case reflect.Int:
		// 		rvParams[i] = reflect.ValueOf(123) // 第i个参数,int类型
		// 		// 其他类型 继续case
		// 	}
		// }
		// // 调用
		// rm.Func.Call(rvParams)

	}
}

2. 通过反射对象的reflect.Value 即 reflect.ValueOf(data) 来调用对象方法

这种方式调用方法套路和第一种都差不多,只是调用的对象不一样而已。

这次我们来个简单的示例,即 通过反射调用 time.Time对象中的 IsZero方法并获取其返回结果


// 利用反射调用对象中的 IsZero方法并返回结果 示例
func ObjIsZero(data interface{}) bool {
	rv := reflect.ValueOf(data)
	rvk := rv.Kind()
	switch rvk {
	case reflect.Struct:
		// 这里通过方法名称来获取 reflect.Value 对象,
		mIsZerRv := rv.MethodByName("IsZero")
		// 调用.IsValid()来验证对象是否可用
		if mIsZerRv.IsValid() {
			// 动态调用结构体中的 IsZero 方法
			rt := mIsZerRv.Call([]reflect.Value{}) // call后面的参数即是要调用方法的参数 []reflect.Value{} 表示无参数
			return len(rt) > 0 && rt[0].Bool()     // IsZero方法的返回值只有一个bool,
		}

	}
	return false
}

总结:

反射中的2种途径都可以调用对象的方法, 一种是通过先获取方法的数量,然后循环调用全部方法, 另外一种是通过具体的方法名称直接获取 reflect.Value 后调用对象方法。这个过程中需要注意的是,如果对象为指针类型,则需要先调用 .Elem() 方法获取指针指向的具体值后再操作。  而对于有参数的方法调用相对复杂,这个一般不常用,知道大概流程即可。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言中的reflect包提供了一种对程序的静态类型进行操作的方法,即可以在程序运行时动态调用、检查和更改变量、类型和方法。 首先,reflect包提供了两个重要的类型:Type和Value。Type表示变量的类型,Value则表示变量的值。可以通过reflect.TypeOf和reflect.ValueOf来获取一个变量的Type和Value。 使用reflect包,我们可以在运行时获取变量的类型和值的一些基本信息,例如判断一个变量是否是某个特定类型,或者获取一个变量的名称和值。这在某些情况下可能是非常有用的,比如在编写通用的函数时,需要对不同类型的变量做相同的处理。 此外,reflect包还提供了一些函数来获取、设置和调用变量、类型和方法的具体信息。可以使用reflect.Value的相关方法来获取和设置变量的值,也可以使用reflect.Type的相关方法来获取类型的信息。使用reflect包还可以动态调用某个值的方法。 需要注意的是,使用reflect包可能会导致一些性能上的损失,因为在运行时需要通过反射来获取变量的信息。因此,在性能要求较高的场景下,尽量避免使用反射。 总结而言,reflect包为我们提供了一种在运行时对变量、类型和方法进行操作的方法,可以通过反射来获取、设置和调用它们的信息。但是,需要注意在性能要求较高的情况下,尽量避免使用反射

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值