Go REFLECT Library | 04 - 反射的值 Value

简介: Go REFLECT Library | 04 - 反射的值 Value

四、从反射值对象获取结构体字段的值

如果变量是结构体类型,那么使用 ValueOf 函数返回的 reflect.Value 类型有以下几种方法可以获取结构体中的字段的值

方法名以及返回值类型 方法说明
Field(i int) Value 根据索引,返回对应结构体字段的 reflect.Value 对象,接着可以再获取到字段的类型和值。当值不是结构体或者索引越界会引发 panic
NumberField() int 返回结构体成员字段数量,当值不是结构体或者索引越界会引发 panic
FieldByName(name string) Value 通过字段名获取指定字段的反射值对象,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic
FieldByIndex(index []int) Value 多层成员访问时,通过索引切片中的索引一层层获取指定索引的反射值对象,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic
FieldByNameFunc(match func(string) bool) Value 根据匹配函数匹配需要的字段,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic
package main
import (
   "fmt"
   "reflect"
)
func main(){
   t := Teacher{"Stark", 33, "NYC"}
   s := Stu{"Peter", 18, "HighSchool","M", t}
   stuValueOf := reflect.ValueOf(s)
   fmt.Printf("stuValueOf 的类型为:%T\n", stuValueOf)
   // 结构体字段个数
   fmt.Printf("通过结构体反射值对象获取到结构体的字段个数为:%v\n", stuValueOf.NumField())
   // 使用不同方法获取结构体字段的反射值对象
   stuValueOfName := stuValueOf.FieldByName("Name")
   stuValueOfAge := stuValueOf.Field(1)
   stuValueOfTeaName := stuValueOf.FieldByIndex([]int{4, 0})
   fmt.Printf("stuValueOfName 的类型为:%T\n", stuValueOfName)
   fmt.Printf("stuValueOfAge 的类型为:%T\n", stuValueOfAge)
   fmt.Printf("stuValueOfTeaName 的类型为:%T\n", stuValueOfTeaName)
   // 再从字段的反射值对象获取原值,可以使用 Interface() 返回获取到接口类型在通过接口断言转换为具体类型
   // 也可以直接通过 String() Int() Float() Bool() 等方法直接进行转换
   stuName := stuValueOfName.String()
   stuAge := stuValueOfAge.Interface().(int) // 强制类型转换,从 int64 转为 int
   stuTeaName := stuValueOfTeaName.String()
   fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Name 字段的值为:%v, 类型为:%T\n", stuName, stuName)
   fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Age 字段的值为:%v, 类型为:%T\n", stuAge, stuAge)
   fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Teacher 字段(结构体) 中的 Name 的值为:%v, 类型为:%T\n", stuTeaName, stuTeaName)
}
type Stu struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Grade string `json:"grade"`
   Gender string `json:"gender"`
   Teacher
}
type Teacher struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Address string `json:"address"`
}
复制代码

执行上述代码,输出结果如下:

stuValueOf 的类型为:reflect.Value
通过结构体反射值对象获取到结构体的字段个数为:5
stuValueOfName 的类型为:reflect.Value
stuValueOfAge 的类型为:reflect.Value
stuValueOfTeaName 的类型为:reflect.Value
从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Name 字段的值为:Peter, 类型为:string
从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Age 字段的值为:18, 类型为:int
从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Teacher 字段(结构体) 中的 Name 的值为:Stark, 类型为:string
复制代码

需要注意的是在使用 FieldByName 方法获取指定名字的结构体字段时,入参要写大写的字段名,不要写 json 标签中的字段名。

五、反射值对象空判断和有效性判断

上篇文章中讲到了在获取了结构体字段的反射值对象或者是基本数据类型变量的反射值对象之后获取原数据的操作,但是在获取之前为避免报错可以先进行值是否为空判断或者有效性的判断。

值是否为空判断和有效性判断需要用到 reflect.Value 反射值对象的下面两个方法:

方法名以及返回值 方法说明
IsNil() bool 返回值是否为 nil,如果值类型不是通道 channel、函数、接口、map、指针或者切片时会发生 panic 错误
IsValid() bool 判断值是否有效,当值本身非法时,返回 false,如果返回值对象不包含任何值,值为 nil
package main
import (
   "fmt"
   "reflect"
)
func main() {
   // 判断结构体指针的反射值对象是否有效
   var zulu *int
   zuluValueOf := reflect.ValueOf(zulu)
   fmt.Printf("Int 指针变量 zulu 的反射值对象是否为空:%v\n", zuluValueOf.IsNil())
   fmt.Printf("Int 指针变量 zulu 的反射值对象是否有效:%v\n", zuluValueOf.IsValid())
   // 空指针
   fmt.Printf("Int 指针变量 zulu 通过反射值对象获取的 zulu 指针变量指向的对象是否有效:%v\n", zuluValueOf.Elem().IsValid())
   // 判断 Map 的 键是否有效
   m := map[string]string{}
   mValueOf := reflect.ValueOf(m)
   // 通过字符串类型的键 name 的 反射值对象获取该键对应的值的反射值对象
   kValueOf := mValueOf.MapIndex(reflect.ValueOf("name"))
   fmt.Printf("m 变量中键 name 对应的值的反射值对象是否有效:%v\n", kValueOf.IsValid())
   // 实例化一个 Teacher 结构体
   t := Teacher{"Stark", 33, "NYC"}
   tValueOf := reflect.ValueOf(t)
   // 获取结构体中不存在的字段
   balanceValueOf := tValueOf.FieldByName("Balance")
   methodValueOf := tValueOf.MethodByName("Teach")
   fmt.Printf("t 结构体实例化对象的反射值获取的 balance 字段的反射值是否有效:%v\n", balanceValueOf.IsValid())
   fmt.Printf("t 结构体实例化对象的反射值获取的 Teach 结构体方法是否有效:%v\n", methodValueOf.IsValid())
}
type Teacher struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Address string `json:"address"`
}
复制代码

执行上述代码,输出结果如下:

Int 指针变量 zulu 的反射值对象是否为空:true
Int 指针变量 zulu 的反射值对象是否有效:true
Int 指针变量 zulu 通过反射值对象获取的 zulu 指针变量指向的对象是否有效:false
m 变量中键 name 对应的值的反射值对象是否有效:false
t 结构体实例化对象的反射值获取的 balance 字段的反射值是否有效:false
t 结构体实例化对象的反射值获取的 Teach 结构体方法是否有效:false


相关文章
|
2月前
|
Go
Go to Learn Go之反射
Go to Learn Go之反射
38 8
|
2月前
|
Go 索引
internal\model\data_support.go:17:10: cannot use _ as value or type
internal\model\data_support.go:17:10: cannot use _ as value or type
|
3月前
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
38 4
|
3月前
|
存储 人工智能 Java
深入理解 go reflect - 要不要传指针
深入理解 go reflect - 要不要传指针
17 0
|
3月前
|
存储 缓存 人工智能
深入理解 go reflect - 反射为什么慢
深入理解 go reflect - 反射为什么慢
30 0
|
3月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
49 0
|
3月前
|
JavaScript 前端开发 Go
Go中使用反射的动态方法调用
Go中使用反射的动态方法调用
|
6月前
|
JSON 监控 安全
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
85 2
|
6月前
|
Go 数据处理
【Go 语言专栏】Go 语言的反射机制及其应用
【4月更文挑战第30天】Go语言的反射机制通过`reflect`包实现,允许运行时检查和操作类型信息。核心概念包括`reflect.Type`(表示类型)和`reflect.Value`(表示值)。主要操作包括获取类型信息、字段信息及动态调用方法。反射适用于通用数据处理、序列化、动态配置和代码生成等场景,但也带来性能开销和维护难度,使用时需谨慎。通过实例展示了如何使用反射处理不同类型数据,强调了在理解和应用反射时需要不断实践。
47 0
|
JSON Go 数据库
go 反射应用
go 反射应用实例
153 0