携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
reflect反射的使用
reflect.Type和reflect.Value
go语言中反射是由reflect包提供的,它定义了两个重要的类型,Type和Value。
1、一个reflect.Type表示一个Go类型。
函数reflect.TypeOf接受任意类型的 interface{}类型,并以reflect.Type形式返回其动态类型;
t := reflect.TypeOf(1)
fmt.Println(t) //int
fmt.Println(t.String()) //int
其中TypeOf(1)传给interface{}参数,将一个值转为接口类型会有一个隐式的接口转换操作。该操作会创建一个包含两个信息的接口值:操作数的动态类型(这里是int)和它的动态的值(这里是1)
2、一个reflect.Value可以装载任意类型的值。
函数reflect.ValueOf接受任意的interface{}类型,并返回一个装载着其动态值的reflect.Value。
v := reflect.ValueOf(1)
fmt.Println(v) //1
fmt.Println(v.String()) //<int Value>
reflect.MethodByName()
reflect.MethByName用于获取与具有给定名称的v方法相对应的函数值。
用法:
func (v Value) MethodByName(name string) Value
package main
import (
"fmt"
"reflect"
)
// Main function
type T struct {}
func (t *T) Hello() {
fmt.Println("Hello World")
}
func main() {
var t T
reflect.ValueOf(&t).MethodByName("Hello").Call([]reflect.Value{})
}
call()函数
在反射中,函数 和方法的类型(Type)都是reflect.Func,如果要调用函数的话,可以通过Value的Call方法。
package main
import (
"fmt"
"reflect"
)
func hello(){
fmt.Println("Hello World!")
}
func main() {
f := hello
rv := reflect.ValueOf(f)
fmt.Println(rv.Kind()) // func
rv.Call(nil) //Hello World!
}
Call()方法的参数是一个Value的slice,对应的反射函数类型的参数,返回值也是一个Value的slice,也是对应反射函数类型的返回值。
如果反射值对象(reflect.Value)中值的类型为函数时,可以通过reflect.Value调用该函数。使用反射调用函数时,需要将参数使用反射值对象的切片[]reflect.Value构造后传入Call()中,调用完成后,函数的返回值通过[]reflect.Value返回。
reflect.IsValid()
reflect.IsValid()函数用于检查v是否有效。当值本身非法时,返回false,例如reflect Value不包含任何值,值为nil等。
用法:
func (v Value) IsValid() bool
示例:
rv := reflect.ValueOf(f)
fmt.Printf("rv IsVaild :%t\n", rv.IsValid()) //rv IsVaild :true
reflect.IsNil()
reflect.IsNil()函数用于检查v是否为nil。返回值是bool类型,如果值类型不是通道、函数、接口、map、指针或切片时发生panic。
reflect.Kind()
在反射包中还有一种重要的类型,称为Kind,反射包中Kind和Type类型相似,但他们也有一定区别,示例如下:
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
}
func main() {
u := User{
Id: 1,
Name: "test",
}
Hello(u)
}
func Hello(u interface{}) {
t := reflect.TypeOf(u)
fmt.Println("u type of ", t) //u type of main.User
fmt.Println("u kind", t.Kind()) //u kind struct
}
通过上面的示例可以看出
- Type表示interface{}的实际类型
- Kind表示特定类型。
总结
这里只是介绍了反射的一些基本用法,还有很多方法没有涉及,可以参考文档进一步的学习,反射在Go中是一个非常强大和先进的概念,当然也不是没有弊端的,使用反射编写清晰和可维护的代码是非常困难的。在任何可能的情况下都应该避免使用,应该谨慎使用,只有在绝对必要的时候才应该使用。反射会使得代码执行效率较慢,原因有:
1、涉及到内存分配以及后续的垃圾回收
2、 reflect实现里面有大量的枚举,也就是for循环,比如类型之类的