go语言中的反射

Golang中的反射是通过refect实现的,我们今天就来看看他的使用和注意事项。

import "reflect"

//reflect包实现了运行时反射,允许程序操作任意类型的对象。 //典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息, //该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值, //该值代表运行时的数据。Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。 首先我们来查看官方文档中关于他的介绍,从这里我们可以看到,他的使用场景跟interface是分不开的,然后还可以了解到他的基本方法包含一个返回TypeOf一个ValueOf。然后我们来继续往下看他的TypeOf和ValueOf的使用。

TypeOf && ValueOf:

image.png

image.png

image.png

从这里可以看到他们接受一个interface参数返回一个type OR Value 类型,我们来测试一把:

返回值:

在这里我们可以清楚的看到他的返回值及其类型,他会自动转化为reflect类型的。这里很重要小伙伴们要注意哦,虽然你看到的返回值是float64没错。而且他也返回了type为float64其实他的类型已经被转变。

接下来我们在看看他的其他方法,其中开局我们提到的interface在这里我们还没有使用。
复制代码

如何获取接口interface中的信息的呢,话不多说上实例:

1) 已知原有类型

image.png

扫描二维码关注公众号,回复: 13620273 查看本文章

image.png

在这里我们先将参数转化为reflect类型,然后通过Interface方法将其转化为interface类型,然后使用类型断言来将变量指回到flioat64类型。这里需要注意的是他不仅可以对值使用而且可以对地址使用,从上例中我们可以看到他并没有创建一个新的对象而是继续指回到原有对象地址。
复制代码

总结: 转换的时候,如果类型不完全符合,直接会触发panic,这里需要注意的是完全符合。 转换的时候,是区分指针和值的 反射是可以将反射对象转化为接口类型对象的 2)不知原有类型 package main

import (
        "fmt"
        "reflect"
)

type User struct {
        Id   int
        Name string
        Age  int
}

func (u User) ReflectCallFunc() {
        fmt.Println("Allen.Wu ReflectCallFunc")
}

func main() {

        user := User{1, "Allen.Wu", 25}

        DoFiledAndMethod(user)

}
复制代码

// 通过接口来获取任意参数,然后一一揭晓 func DoFiledAndMethod(input interface{}) {

getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())

getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)

// 获取方法字段
// 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
// 2. 再通过reflect.Type的Field获取其Field
// 3. 最后通过Field的Interface()得到对应的value
for i := 0; i < getType.NumField(); i++ {
	field := getType.Field(i)
	value := getValue.Field(i).Interface()
	fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}

// 获取方法
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
for i := 0; i < getType.NumMethod(); i++ {
	m := getType.Method(i)
	fmt.Printf("%s: %v\n", m.Name, m.Type)
	//使用方法
	getaddrValue := reflect.ValueOf(input)
	getaddrValue.MethodByName(m.Name).Call(nil)
}
}
复制代码

输出部分:

image.png

我们可以看到以上示例中,我们先定义了一个User结构体并且赋予了他 ReflectCallFunc方法,然后我们在后边的操作用尝试是用反射来获取并使用他的方法。

属性获取:我们通过NumField方法来获取这个结构体所包含的属性个数并以此进行遍历,然后通过Field方法取值并打印。

方法获取:我们通过NumMethod方法来获取这个结构体所包含的方法个数并以此进行遍历,然后通过Method方法可以获取到struct中方法的对象,我们后边进行了方法的调用,直接针对刚才获取的方法对象使用Call方法来调用,因为这里我们的方法是无参数的,所以我们直接给call中传入一个nil即可,如果这是的方法是包含参数的,那么我们需要构造一个[]reflect.Value切片,在切片中append我们需要传入的参数,需要注意的是这里传入的参数依然为reflect类型对象,如下方式使用:

params := make([]reflect.Value, 1) params[0] = reflect.ValueOf(18) mtV.MethodByName("SetI").Call(params) 如何修改interface中的变量值

image.png

image.png

用指针传递了,因为只有以指针方式传递才能够修改我们的实际变量。 返回展示:

如上我们使用了指针传递的方式来使用relect方法,这里我们使用elem()方法来重新指会数据的原类型,那么有的童鞋可能要稳了Elem方法是个什么东西呢,我们来带大家看一下

image.png

看到这里可能又有疑问Kind又是个什么东西呢

image.png

看到这里我相信大家应该就懂了,原来我们的kind方法就是返回一个值的分类。

那么有的同学又要问了,我们不是已经拿到原有值得指针了么为什么还是调用elem方法来进行重新指定呢,不要忘了我们前边说到的,这里reflect会重新定义为refect对象类型而不是我们的原有值类型,所以这里我们还需要拿到他在进一步的值确认操作。

好,继续回到我们的正题,继续解析我们上边的代码,当我们使用elem方法获取了value原本值后就可以继续我们后边的操作了,接下来调用我们的setfloat方法对我们的值进行修改就可以了,这里为什么用setfloat方法呢,因为我们传进来的就是一个float类型的参数啊,相关的方法还有很多,童靴们可以自行去文档中查看他的一个相关使用。

image.png

Golang中的reflect性能, 因为在使用过程中我们的relect.value是一个具体的值,而不是一个可以复用的反射对象,所以每次反射都要重新对其进行重新分配,还涉及到GC,而且在使用过程中多使用大量循环,所以其性能并不是很好。童靴们在使用过程中应注意不要用力过猛哦。

好,今天我们对于Golang中反射的介绍就到这里,其他还有很多内容官方文档中都有较好的解释,如果有什么不懂得欢迎小伙伴们提问。

猜你喜欢

转载自juejin.im/post/7042666217202515981