Golang反射获取变量类型和值

打印 上一主题 下一主题

主题 972|帖子 972|积分 2918

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 什么是反射

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。
Golang反射包中有两对非常重要的函数和类型,两个函数分别是:
reflect.TypeOf 能获取类型信息reflect.Type
reflect.ValueOf 能获取数据的运行时表示reflect.Value
 
3. reflect.Type

Golang是一门静态类型的语言,反射是建立在类型之上的。
通过reflect.TypeOf() 函数可以获得任意值的类型信息。
 
3.1 类型Type和种类Kind

诸如int32, slice, map以及通过type关键词自定义的类型。
种类Kind可以理解为类型的具体分类。如int32、type MyInt32 int32是两种不同类型,但都属于int32这个种类。
使用 reflect.TypeOf()获取变量类型以及种类。
  1. func main() {
  2.         type MyInt32 int32
  3.         a := MyInt32(1)
  4.         b := int32(1)
  5.         fmt.Printf("reflect.TypeOf(a):%v Kind:%v\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())
  6.         fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", reflect.TypeOf(b), reflect.TypeOf(b).Kind())
  7. }
复制代码
代码输出如下,由此可以看出int32、type MyInt32 int32是两种不同类型,但都属于int32这个种类。
  1. $ go run main.go
  2. reflect.TypeOf(a):main.MyInt32 Kind:int32
  3. reflect.TypeOf(b):int32 Kind:int32
复制代码
种类定义点击查看
  1. // A Kind represents the specific kind of type that a Type represents.
  2. // The zero Kind is not a valid kind.
  3. type Kind uint
  4. const (
  5.         Invalid Kind = iota
  6.         Bool
  7.         Int
  8.         Int8
  9.         Int16
  10.         Int32
  11.         Int64
  12.         Uint
  13.         Uint8
  14.         Uint16
  15.         Uint32
  16.         Uint64
  17.         Uintptr
  18.         Float32
  19.         Float64
  20.         Complex64
  21.         Complex128
  22.         Array
  23.         Chan
  24.         Func
  25.         Interface
  26.         Map
  27.         Pointer
  28.         Slice
  29.         String
  30.         Struct
  31.         UnsafePointer
  32. )
复制代码
 
3.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()函数获取。
  1. func main() {
  2.         type myStruct struct {
  3.         }
  4.         a := &myStruct{}
  5.         typeA := reflect.TypeOf(a)
  6.         fmt.Printf("TypeOf(a):%v Kind:%v\n", typeA, typeA.Kind())
  7.         fmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\n", typeA.Elem(), typeA.Elem().Kind())
  8.         s := []int64{}
  9.         typeS := reflect.TypeOf(s)
  10.         fmt.Printf("TypeOf(s):%v Kind:%v\n", typeS, typeS.Kind())
  11.         fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\n", typeS.Elem(), typeS.Elem().Kind())
  12. }
复制代码
代码输出如下,由此可以看出,通过reflect.Elem()函数可以获取引用指向数据的类型。
  1. $ go run main.go
  2. TypeOf(a):*main.myStruct Kind:ptr
  3. TypeOf(a).Elem():main.myStruct Elem().Kind:struct
  4. TypeOf(s):[]int64 Kind:slice
  5. TypeOf(s).Elem():int64 Elem().Kind:int64
复制代码
 
3.3 结构体成员类型

通过NumField获取成员数量,Field通过下标访问成员的类型信息StructField,包括成员名称、类型、Tag信息等。
  1. func main() {
  2.         type secStruct struct {
  3.                 Cnt []int64
  4.         }
  5.         type myStruct struct {
  6.                 Num   int    `json:"num_json" orm:"column:num_orm"`
  7.                 Desc  string `json:"desc_json" orm:"column:desc_orm"`
  8.                 Child secStruct
  9.         }
  10.         s := myStruct{}
  11.         typeS := reflect.TypeOf(s)
  12.         // 成员数量
  13.         fmt.Printf("NumField:%v \n", typeS.NumField())
  14.         // 每个成员的信息 包括名称、类型、Tag
  15.         for i := 0; i < typeS.NumField(); i++ {
  16.                 // 通过下标访问成员
  17.                 fmt.Printf("Field(%v):%+v\n", i, typeS.Field(i))
  18.         }
  19.         // 通过名称访问成员
  20.         field, ok := typeS.FieldByName("Num")
  21.         fmt.Printf("FieldByName("Num") ok:%v field:%+v\n", ok, field)
  22.         // 获取tag值
  23.         fmt.Printf("json tag val:%+v\n", field.Tag.Get("json"))
  24.         // 获取嵌套结构体的字段
  25.         fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2, 0}))
  26. }
复制代码
代码输出如下,
  1. $ go run main.go
  2. NumField:3
  3. Field(0):{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
  4. Field(1):{Name:Desc PkgPath: Type:string Tag:json:"desc_json" orm:"column:desc_orm" Offset:8 Index:[1] Anonymous:false}
  5. Field(2):{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}
  6. FieldByName("Num") ok:true field:{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
  7. json tag val:num_json
  8. Cnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false}
复制代码
 
4. reflect.Value

通过reflect.ValueOf获取变量值、值类型,种类为Array, Chan, Map, Slice, 或String可通过Len()获取长度
  1. func main() {
  2.         b := int32(1)
  3.         valueB := reflect.ValueOf(b)
  4.         fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", valueB, valueB.Kind())
  5.         s := "abcdefg"
  6.         valueS := reflect.ValueOf(s)
  7.         fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
  8. }
复制代码
代码输出如下,
  1. $ go run main.go
  2. reflect.TypeOf(b):1 Kind:int32
  3. reflect.TypeOf(s):abcdefg Kind:string Len:7
复制代码
 
4.2 结构体的成员的值

3.3 结构体成员类型获取结构体成员类型类似,reflect提供了NumField获取成员数量,Field通过下标访问成员的值。
  1. func main() {
  2.         type secStruct struct {
  3.                 Cnt []int64
  4.         }
  5.         type myStruct struct {
  6.                 Num   int    `json:"num_json" orm:"column:num_orm"`
  7.                 Desc  string `json:"desc_json" orm:"column:desc_orm"`
  8.                 Child secStruct
  9.         }
  10.         s := myStruct{
  11.                 Num:   100,
  12.                 Desc:  "desc",
  13.                 Child: secStruct{[]int64{1, 2, 3}},
  14.         }
  15.         valueS := reflect.ValueOf(s)
  16.         // 成员数量
  17.         fmt.Printf("NumField:%v \n", valueS.NumField())
  18.         // 每个成员的值
  19.         for i := 0; i < valueS.NumField(); i++ {
  20.                 // 通过下标访问成员
  21.                 fmt.Printf("value(%v):%+v\n", i, valueS.Field(i))
  22.         }
  23.         // 通过名称访问成员
  24.         value := valueS.FieldByName("Num")
  25.         fmt.Printf("FieldByName("Num") value:%v\n", value)
  26.         // 获取嵌套结构体的字段
  27.         fmt.Printf("Cnt field:%+v\n", valueS.FieldByIndex([]int{2, 0}))
  28. }
复制代码
代码输出如下
  1. $ go run main.go
  2. NumField:3
  3. value(0):100
  4. value(1):desc
  5. value(2):{Cnt:[1 2 3]}
  6. FieldByName("Num") value:100
  7. Cnt field:[1 2 3]
复制代码
 
4.3 遍历array、slice

通过func (v Value) Index(i int) Value可以通过下标来访问Array, Slice,或者 String各个元素的值。
  1. func main() {
  2.         s := []int64{1, 2, 3, 4, 5, 6}
  3.         valueS := reflect.ValueOf(s)
  4.         fmt.Printf("ValueOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
  5.         for i := 0; i < valueS.Len(); i++ {
  6.                 fmt.Printf("valueS.Index(%v):%v\n", i, valueS.Index(i))
  7.         }
  8. }
复制代码
代码输出如下
  1. $ go run main.go
  2. ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6
  3. valueS.Index(0):1
  4. valueS.Index(1):2
  5. valueS.Index(2):3
  6. valueS.Index(3):4
  7. valueS.Index(4):5
  8. valueS.Index(5):6
复制代码
 
4.4 遍历map

reflect有两种方法遍历map

  • 通过迭代器MapIter遍历map
  • 先获取map的所有key,再通过key获取对应的value
  1. func main() {
  2.         m := map[int]string{
  3.                 1: "1",
  4.                 2: "2",
  5.                 3: "3",
  6.         }
  7.         valueM := reflect.ValueOf(m)
  8.         // 迭代器访问
  9.         iter := valueM.MapRange()
  10.         for iter.Next() {
  11.                 fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())
  12.         }
  13.         fmt.Println("------")
  14.         // 通过key访问
  15.         keys := valueM.MapKeys()
  16.         for i := 0; i < len(keys); i++ {
  17.                 fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))
  18.         }
  19. }
复制代码
代码输出如下,
  1. $ go run main.go
  2. key:1 val:1
  3. key:2 val:2
  4. key:3 val:3
  5. ------
  6. key:3 val:3
  7. key:1 val:1
  8. key:2 val:2
复制代码
 
5. 反射的三大定律

反射的两个基础函数定义,

  • 获取类型func TypeOf(i any) Type
  • 获取值func ValueOf(i any) Value
其中,any是interface{}的别名。
interface{}是不包含任何方法签名的空接口,任何类型都实现了空接口。
A value of interface type can hold any value that implements those methods.
因此,interface{}可以承载任何变量的 (value, concrete type)信息。
 
5.1 从interface到反射对象

interface{}承载变量的(value, concrete type)信息,通过反射暴露方法来访问interface{}的值和类型。
可以简单理解为interface{}的值和信息传递到reflect.Type和 reflect.Value,方便获取。
 
5.2 从反射对象到interface

可以通过函数func (v Value) Interface() (i any)将反射对象转换为interface{},
是func ValueOf(i any) Value的反向操作。
  1. func main() {
  2.         a := int32(10)
  3.         valueA := reflect.ValueOf(a)
  4.         fmt.Printf("ValueOf(a):%v\n", valueA)
  5.         fmt.Printf("Interface():%v\n", valueA.Interface())
  6.         ai, ok := valueA.Interface().(int32)
  7.         fmt.Printf("ok:%v val:%v\n", ok, ai)
  8. }
复制代码
代码输出如下
  1. $ go run main.go
  2. ValueOf(a):10
  3. Interface():10
  4. ok:true val:10
复制代码
 
5.3 通过反射修改对象,该对象值必须是可修改的

reflect提供func (v Value) CanSet() bool判断对象值是否修改,通过func (v Value) Set(x Value)修改对象值
  1. func main() {
  2.         a := int32(10)
  3.         valueA := reflect.ValueOf(a)
  4.         fmt.Printf("valueA :%v\n", valueA.CanSet())
  5.         b := int32(100)
  6.         valuePtrB := reflect.ValueOf(&b)
  7.         fmt.Printf("valuePtrB:%v Elem:%v\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())
  8.         valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))
  9.         fmt.Printf("b:%v Elem:%v\n", b, valuePtrB.Elem())
  10. }
复制代码
 代码输出如下
  1. $ go run main.go
  2. valueA :false
  3. valuePtrB:false Elem:true
  4. b:200 Elem:200
复制代码
后续章节再分享通过修改各种类型的值的实操。
 
5. 参考文档

laws-of-reflection
interface

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表