Go语言通过反射获取各种类型变量的值
反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。
1、什么是反射
反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。
Golang 反射包中有两对非常重要的函数和类型,两个函数分别是:
-
reflect.TypeOf 能获取类型信息 reflect.Type;
-
reflect.ValueOf 能获取数据的运行时表示 reflect.Value;
2、reflect.Type
Golang 是一门静态类型的语言,反射是建立在类型之上的。
通过 reflect.TypeOf() 函数可以获得任意值的类型信息。
2.1 类型Type和种类Kind
诸如 int32, slice, map 以及通过 type 关键词自定义的类型。
种类 Kind 可以理解为类型的具体分类,如 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种
类。
使用 reflect.TypeOf() 获取变量类型以及种类。
package main
import (
"fmt"
"reflect"
)
func main() {
type MyInt32 int32
a := MyInt32(1)
b := int32(1)
// reflect.TypeOf(a):main.MyInt32 Kind:int32
fmt.Printf("reflect.TypeOf(a):%v Kind:%v\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())
// reflect.TypeOf(b):int32 Kind:int32
fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", reflect.TypeOf(b), reflect.TypeOf(b).Kind())
}
从代码输出可以看出 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种类。
种类定义点击查看:
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)
2.2 引用指向元素的类型
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
Elem() Type
部分情况我们需要获取指针指向元素的类型、或者 slice 元素的类型,可以 reflect.Elem() 函数获取。
package main
import (
"fmt"
"reflect"
)
type myStruct struct {
}
func main() {
a := &myStruct{}
typeA := reflect.TypeOf(a)
// TypeOf(a):*main.myStruct Kind:ptr
fmt.Printf("TypeOf(a):%v Kind:%v\n", typeA, typeA.Kind())
// TypeOf(a).Elem():main.myStruct Elem().Kind:struct
fmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\n", typeA.Elem(), typeA.Elem().Kind())
s := []int64{}
typeS := reflect.TypeOf(s)
// TypeOf(s):[]int64 Kind:slice
fmt.Printf("TypeOf(s):%v Kind:%v\n", typeS, typeS.Kind())
// TypeOf(s).Elem():int64 Elem().Kind:int64
fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\n", typeS.Elem(), typeS.Elem().Kind())
}
从代码输出可以看出,通过 reflect.Elem() 函数可以获取引用指向数据的类型。
2.3 结构体成员类型
通过 NumField 获取成员数量,Field 通过下标访问成员的类型信息 StructField,包括成员名称、类型、Tag 信息
等。
package main
import (
"fmt"
"reflect"
)
type secStruct struct {
Cnt []int64
}
type myStruct struct {
Num int `json:"num_json" orm:"column:num_orm"`
Desc string `json:"desc_json" orm:"column:desc_orm"`
Child secStruct
}
func main() {
s := myStruct{}
typeS := reflect.TypeOf(s)
// 成员数量
// NumField:3
fmt.Printf("NumField:%v \n", typeS.NumField())
// 每个成员的信息 包括名称、类型、Tag
for i := 0; i < typeS.NumField(); i++ {
// 通过下标访问成员
// Field(0):{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
// Field(1):{Name:Desc PkgPath: Type:string Tag:json:"desc_json" orm:"column:desc_orm" Offset:8 Index:[1] Anonymous:false}
// Field(2):{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}
fmt.Printf("Field(%v):%+v\n", i, typeS.Field(i))
}
// 通过名称访问成员
field, ok := typeS.FieldByName("Num")
// FieldByName("Num") ok:true field:{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
fmt.Printf("FieldByName(\"Num\") ok:%v field:%+v\n", ok, field)
// 获取tag值
// json tag val:num_json
fmt.Printf("json tag val:%+v\n", field.Tag.Get("json"))
if value, ok := field.Tag.Lookup("orm"); ok {
// rm tag val:column:num_orm
fmt.Printf("orm tag val:%+v\n", value)
}
// 获取嵌套结构体的字段
// Cnt field:{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}
fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2}))
// Cnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false}
fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2, 0}))
}
3、reflect.Value
通过 reflect.ValueOf 获取变量值、值类型,种类为 Array, Chan, Map, Slice 或 String,可通过 Len() 获取长度。
package main
import (
"fmt"
"reflect"
)
func main() {
b := int32(1)
valueB := reflect.ValueOf(b)
// reflect.TypeOf(b):1 Kind:int32
fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", valueB, valueB.Kind())
s := "abcdefg"
valueS := reflect.ValueOf(s)
// reflect.TypeOf(s):abcdefg Kind:string Len:7
fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
}
3.1 结构体的成员的值
和 2.3 结构体成员类型获取结构体成员类型类似,reflect 提供了 NumField 获取成员数量,Field 通过下标访问成
员的值。
package main
import (
"fmt"
"reflect"
)
type secStruct struct {
Cnt []int64
}
type myStruct struct {
Num int `json:"num_json" orm:"column:num_orm"`
Desc string `json:"desc_json" orm:"column:desc_orm"`
Child secStruct
}
func main() {
s := myStruct{
Num: 100,
Desc: "desc",
Child: secStruct{[]int64{1, 2, 3}},
}
valueS := reflect.ValueOf(s)
// 成员数量
// NumField:3
fmt.Printf("NumField:%v \n", valueS.NumField())
// 每个成员的值
for i := 0; i < valueS.NumField(); i++ {
// 通过下标访问成员
// value(0):100
// value(1):desc
// value(2):{Cnt:[1 2 3]}
fmt.Printf("value(%v):%+v\n", i, valueS.Field(i))
}
// 通过名称访问成员
value := valueS.FieldByName("Num")
// FieldByName("Num") value:100
fmt.Printf("FieldByName(\"Num\") value:%v\n", value)
// 获取嵌套结构体的字段
// Cnt field:[1 2 3]
fmt.Printf("Cnt field:%+v\n", valueS.FieldByIndex([]int{2, 0}))
}
3.2 遍历array、slice
通过 func (v Value) Index(i int) Value 可以通过下标来访问 Array,Slice,或者 String 各个元素的值。
package main
import (
"fmt"
"reflect"
)
func main() {
s := []int64{1, 2, 3, 4, 5, 6}
valueS := reflect.ValueOf(s)
// ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6
fmt.Printf("ValueOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
for i := 0; i < valueS.Len(); i++ {
// valueS.Index(0):1
// valueS.Index(1):2
// valueS.Index(2):3
// valueS.Index(3):4
// valueS.Index(4):5
// valueS.Index(5):6
fmt.Printf("valueS.Index(%v):%v\n", i, valueS.Index(i))
}
}
3.3 遍历map
reflect 有两种方法遍历 map
-
通过迭代器 MapIter 遍历 map
-
先获取 map 的所有 key,再通过 key 获取对应的 value
package main
import (
"fmt"
"reflect"
)
func main() {
m := map[int]string{
1: "1",
2: "2",
3: "3",
}
valueM := reflect.ValueOf(m)
// 迭代器访问
iter := valueM.MapRange()
for iter.Next() {
// key:1 val:1
// key:2 val:2
// key:3 val:3
fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())
}
// ------
fmt.Println("------")
// 通过key访问
keys := valueM.MapKeys()
for i := 0; i < len(keys); i++ {
// key:1 val:1
// key:2 val:2
// key:3 val:3
fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))
}
}
4、反射的三大定律
反射的两个基础函数定义:
- 获取类型 func TypeOf(i any) Type
- 获取值 func ValueOf(i any) Value
其中,any 是 interface{} 的别名。
interface{} 是不包含任何方法签名的空接口,任何类型都实现了空接口。
因此,interface{} 可以承载任何变量的 (value, concrete type)信息。
4.1 从interface到反射对象
interface{} 承载变量的 (value, concrete type) 信息,通过反射暴露方法来访问 interface{} 的值和类型。
可以简单理解为 interface{} 的值和信息传递到 reflect.Type 和 reflect.Value,方便获取。
4.2 从反射对象到interface
可以通过函数 func (v Value) Interface() (i any) 将反射对象转换为 interface{},是 func ValueOf(i any) Value的反
向操作。文章来源:https://www.toymoban.com/news/detail-608378.html
package main
import (
"fmt"
"reflect"
)
func main() {
a := int32(10)
valueA := reflect.ValueOf(a)
// ValueOf(a):10
fmt.Printf("ValueOf(a):%v\n", valueA)
// Interface():10
fmt.Printf("Interface():%v\n", valueA.Interface())
ai, ok := valueA.Interface().(int32)
// ok:true val:10
fmt.Printf("ok:%v val:%v\n", ok, ai)
}
4.3 通过反射修改对象,该对象值必须是可修改的
reflect提供 func (v Value) CanSet() bool 判断对象值是否修改,通过 func (v Value) Set(x Value) 修改对象值。文章来源地址https://www.toymoban.com/news/detail-608378.html
package main
import (
"fmt"
"reflect"
)
func main() {
a := int32(10)
valueA := reflect.ValueOf(a)
// valueA :false
fmt.Printf("valueA :%v\n", valueA.CanSet())
b := int32(100)
valuePtrB := reflect.ValueOf(&b)
// valuePtrB:false Elem:true
fmt.Printf("valuePtrB:%v Elem:%v\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())
valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))
// b:200 Elem:200
fmt.Printf("b:%v Elem:%v\n", b, valuePtrB.Elem())
}
到了这里,关于Go语言通过反射获取各种类型变量的值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!