1.反射介绍

反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。
支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
Go程序在运行期使用reflect包访问程序的反射信息。

2.reflect包

在Go语言的反射机制中,任何接口值都由是一个具体类型具体类型的值两部分组成的。 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,
并且reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。

2.1TypeOf

使用reflect.TypeOf()函数可以获得任意值的类型对象(reflect.Type)

  1. import (
  2. "fmt"
  3. "reflect"
  4. )
  5. func reflectType(x interface{}) {
  6. v := reflect.TypeOf(x)
  7. fmt.Printf("%v\n",v)
  8. }
  9. func main() {
  10. var a float32 = 3.14
  11. reflectType(a) //type:float32
  12. var b int64 = 100
  13. reflectType(b) //type:int64
  14. }
  1. type MyInt int64
  2. func reflectType(x interface{}) {
  3. t := reflect.TypeOf(x)
  4. fmt.Printf("%v %v\n",t.Name(),t.Kind())
  5. }
  6. func main() {
  7. var a *float32
  8. var b MyInt
  9. var c rune
  10. reflectType(a) //ptr
  11. reflectType(b) //type:myInt kind:int64
  12. reflectType(c) //type:int32 kind:int32
  13. }
  1. type person struct {
  2. name string
  3. age int
  4. }
  5. type book struct {
  6. title string
  7. }
  8. var p=person{
  9. name:"cheng",
  10. age:18,
  11. }
  12. var b = book{title: "Golang"}
  13. reflectType(p) // person struct
  14. reflectType(b) // book struct

在reflect包中定义的Kind类型如下

  1. type Kind uint
  2. const (
  3. Invalid Kind = iota // 非法类型
  4. Bool // 布尔型
  5. Int // 有符号整型
  6. Int8 // 有符号8位整型
  7. Int16 // 有符号16位整型
  8. Int32 // 有符号32位整型
  9. Int64 // 有符号64位整型
  10. Uint // 无符号整型
  11. Uint8 // 无符号8位整型
  12. Uint16 // 无符号16位整型
  13. Uint32 // 无符号32位整型
  14. Uint64 // 无符号64位整型
  15. Uintptr // 指针
  16. Float32 // 单精度浮点数
  17. Float64 // 双精度浮点数
  18. Complex64 // 64位复数类型
  19. Complex128 // 128位复数类型
  20. Array // 数组
  21. Chan // 通道
  22. Func // 函数
  23. Interface // 接口
  24. Map // 映射
  25. Ptr // 指针
  26. Slice // 切片
  27. String // 字符串
  28. Struct // 结构体
  29. UnsafePointer // 底层指针
  30. )

2.2ValueOf

通过反射获取值

  1. func reflectValue(x interface{}) {
  2. v:=reflect.ValueOf(x)
  3. k:=v.Kind()
  4. switch k {
  5. case reflect.Int64:
  6. fmt.Println(int64(v.Int()))
  7. case reflect.Float32:
  8. fmt.Println(float32(v.Float()))
  9. case reflect.Float64:
  10. fmt.Println(float64(v.Float()))
  11. }
  12. }
  1. var a float32 = 3.14
  2. var b int64 = 100
  3. reflectValue(a) //3.14
  4. reflectValue(b) //100
  5. c := reflect.ValueOf(10)
  6. fmt.Printf("%T %v",c,c) //reflect.value 10

通过反射设置变量的值

  1. import (
  2. "fmt"
  3. "reflect"
  4. )
  5. func reflectSetValue(x interface{}) {
  6. v := reflect.ValueOf(x)
  7. if v.Kind() == reflect.Int64{
  8. v.SetInt(200) //修改的是副本
  9. }
  10. }
  11. func reflectSetValues(x interface{}) {
  12. v := reflect.ValueOf(x)
  13. //反射中使用 Elem()方法获取指针对应的值
  14. if v.Elem().Kind() == reflect.Int64{
  15. v.Elem().SetInt(200)
  16. }
  17. }
  18. func main() {
  19. var a int64 = 100
  20. //reflectSetValue(a) panic: reflect: reflect.Value.SetInt using unaddressable value
  21. reflectSetValues(&a)
  22. fmt.Println(a)
  23. }

isNil()

IsNil()常被用于判断指针是否为空

  1. func (v Value) IsNil() bool

isValid()

IsValid()常被用于判定返回值是否有效

  1. func (v Value) IsValid() bool
  1. var a *int //*int类型空指针
  2. //IsNil()常被用于判断指针是否为空
  3. fmt.Println(reflect.ValueOf(a).IsNil()) //true
  4. //IsValid()常被用于判定返回值是否有效
  5. fmt.Println(reflect.ValueOf(nil).IsValid()) //false
  6. b := struct {name string}{}
  7. //b := struct {}{}
  8. fmt.Println(reflect.ValueOf(b).FieldByName("name").IsValid()) //true
  9. fmt.Println(reflect.ValueOf(b).MethodByName("func").IsValid()) //false
  10. c :=map[string]int{}
  11. fmt.Println(reflect.ValueOf(c).MapIndex(reflect.ValueOf("name")).IsValid()) //false

3.结构体反射

当我们使用反射得到一个结构体数据之后可以通过索引依次获取其字段信息,也可以通过字段名去获取指定的字段信息。

  1. type student struct {
  2. Name string `json:"name"`
  3. Score int `json:"score"`
  4. }
  5. func main() {
  6. stu :=student{
  7. Name: "cheng",
  8. Score: 90,
  9. }
  10. t := reflect.TypeOf(stu)
  11. fmt.Println(t.Name(),t.Kind())
  12. for i := 0; i < t.NumField(); i++ {
  13. field := t.Field(i)
  14. fmt.Printf("name:%s\n",field.Name)
  15. fmt.Printf("name:%d\n",field.Index)
  16. fmt.Printf("name:%v\n",field.Type)
  17. fmt.Printf("name:%v\n",field.Tag.Get("json"))
  18. }
  19. }

4.反射是把双刃剑

反射是一个强大并富有表现力的工具,能让我们写出更灵活的代码。但是反射不应该被滥用,原因有以下三个。

  1. 基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic。
  2. 大量使用反射的代码通常难以理解。
  3. 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。