Golang反射获取变量类型和值

Posted golandhome

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang反射获取变量类型和值相关的知识,希望对你有一定的参考价值。

Golang反射获取变量类型和值

 

1. 什么是反射

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。

Golang反射包中有两对非常重要的函数和类型,两个函数分别是:

reflect.TypeOf能获取类型信息reflect.Type

reflect.ValueOf 能获取数据的运行时表示reflect.Value

 

2. reflect.Type

Golang是一门静态类型的语言,反射是建立在类型之上的。

通过reflect.TypeOf()函数可以获得任意值的类型信息。

 

2.1 类型Type和种类Kind

诸如int32slicemap以及通过type关键词自定义的类型。

种类Kind可以理解为类型的具体分类。如int32type MyInt32 int32是两种不同类型,但都属于int32这个种类。

使用 reflect.TypeOf()获取变量类型以及种类。

  func main()
  type MyInt32 int32
  a := MyInt32(1)
  b := int32(1)
  fmt.Printf("reflect.TypeOf(a):%v Kind:%v\\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())
  fmt.Printf("reflect.TypeOf(b):%v Kind:%v\\n", reflect.TypeOf(b), reflect.TypeOf(b).Kind())
 

代码输出如下,由此可以看出int32type MyInt32 int32是两种不同类型,但都属于int32这个种类。

  $ go run main.go
  reflect.TypeOf(a):main.MyInt32 Kind:int32
  reflect.TypeOf(b):int32 Kind:int32
种类定义点击查看

 

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()函数获取。

  func main()
  type myStruct struct
 
  a := &myStruct
  typeA := reflect.TypeOf(a)
  fmt.Printf("TypeOf(a):%v Kind:%v\\n", typeA, typeA.Kind())
  fmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\\n", typeA.Elem(), typeA.Elem().Kind())
  s := []int64
  typeS := reflect.TypeOf(s)
  fmt.Printf("TypeOf(s):%v Kind:%v\\n", typeS, typeS.Kind())
  fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\\n", typeS.Elem(), typeS.Elem().Kind())
 

代码输出如下,由此可以看出,通过reflect.Elem()函数可以获取引用指向数据的类型。

  $ go run main.go
  TypeOf(a):*main.myStruct Kind:ptr
  TypeOf(a).Elem():main.myStruct Elem().Kind:struct
  TypeOf(s):[]int64 Kind:slice
  TypeOf(s).Elem():int64 Elem().Kind:int64

 

2.3 结构体成员类型

通过NumField获取成员数量,Field通过下标访问成员的类型信息StructField,包括成员名称、类型、Tag信息等。

  func main()
  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
 
  s := myStruct
  typeS := reflect.TypeOf(s)
  // 成员数量
  fmt.Printf("NumField:%v \\n", typeS.NumField())
  // 每个成员的信息 包括名称、类型、Tag
  for i := 0; i < typeS.NumField(); i++
  // 通过下标访问成员
  fmt.Printf("Field(%v):%+v\\n", i, typeS.Field(i))
 
  // 通过名称访问成员
  field, ok := typeS.FieldByName("Num")
  fmt.Printf("FieldByName(\\"Num\\") ok:%v field:%+v\\n", ok, field)
  // 获取tag值
  fmt.Printf("json tag val:%+v\\n", field.Tag.Get("json"))
  // 获取嵌套结构体的字段
  fmt.Printf("Cnt field:%+v\\n", typeS.FieldByIndex([]int2, 0))
 

代码输出如下,

  $ go run main.go
  NumField:3
  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
  FieldByName("Num") ok:true field:Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false
  json tag val:num_json
  Cnt field:Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false

 

3. reflect.Value

通过reflect.ValueOf获取变量值、值类型,种类为ArrayChanMapSlice, 或String可通过Len()获取长度

  func main()
  b := int32(1)
  valueB := reflect.ValueOf(b)
  fmt.Printf("reflect.TypeOf(b):%v Kind:%v\\n", valueB, valueB.Kind())
  s := "abcdefg"
  valueS := reflect.ValueOf(s)
  fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\\n", valueS, valueS.Kind(), valueS.Len())
 

代码输出如下,

  $ go run main.go
  reflect.TypeOf(b):1 Kind:int32
  reflect.TypeOf(s):abcdefg Kind:string Len:7

 

3.1 结构体的成员的值

3.3 结构体成员类型获取结构体成员类型类似,reflect提供了NumField获取成员数量,Field通过下标访问成员的值。

  func main()
  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
 
  s := myStruct
  Num: 100,
  Desc: "desc",
  Child: secStruct[]int641, 2, 3,
 
  valueS := reflect.ValueOf(s)
  // 成员数量
  fmt.Printf("NumField:%v \\n", valueS.NumField())
  // 每个成员的值
  for i := 0; i < valueS.NumField(); i++
  // 通过下标访问成员
  fmt.Printf("value(%v):%+v\\n", i, valueS.Field(i))
 
  // 通过名称访问成员
  value := valueS.FieldByName("Num")
  fmt.Printf("FieldByName(\\"Num\\") value:%v\\n", value)
  // 获取嵌套结构体的字段
  fmt.Printf("Cnt field:%+v\\n", valueS.FieldByIndex([]int2, 0))
 

代码输出如下

  $ go run main.go
  NumField:3
  value(0):100
  value(1):desc
  value(2):Cnt:[1 2 3]
  FieldByName("Num") value:100
  Cnt field:[1 2 3]

 

3.2 遍历array、slice

通过func (v Value) Index(i int) Value可以通过下标来访问ArraySlice,或者 String各个元素的值。

  func main()
  s := []int641, 2, 3, 4, 5, 6
  valueS := reflect.ValueOf(s)
  fmt.Printf("ValueOf(s):%v Kind:%v Len:%v\\n", valueS, valueS.Kind(), valueS.Len())
  for i := 0; i < valueS.Len(); i++
  fmt.Printf("valueS.Index(%v):%v\\n", i, valueS.Index(i))
 
 

代码输出如下

  $ go run main.go
  ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6
  valueS.Index(0):1
  valueS.Index(1):2
  valueS.Index(2):3
  valueS.Index(3):4
  valueS.Index(4):5
  valueS.Index(5):6

 

3.3 遍历map

reflect有两种方法遍历map

  • 通过迭代器MapIter遍历map
  • 先获取map的所有key,再通过key获取对应的value
  func main()
  m := map[int]string
  1: "1",
  2: "2",
  3: "3",
 
  valueM := reflect.ValueOf(m)
  // 迭代器访问
  iter := valueM.MapRange()
  for iter.Next()
  fmt.Printf("key:%v val:%v\\n", iter.Key(), iter.Value())
 
  fmt.Println("------")
  // 通过key访问
  keys := valueM.MapKeys()
  for i := 0; i < len(keys); i++
  fmt.Printf("key:%v val:%v\\n", keys[i], valueM.MapIndex(keys[i]))
 
 

代码输出如下,

  $ go run main.go
  key:1 val:1
  key:2 val:2
  key:3 val:3
  ------
  key:3 val:3
  key:1 val:1
  key:2 val:2

 

4. 反射的三大定律

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

  • 获取类型func TypeOf(i any) Type
  • 获取值func ValueOf(i any) Value

其中,anyinterface的别名。

interface是不包含任何方法签名的空接口,任何类型都实现了空接口。

A value of interface type can hold any value that implements those methods.

因此,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的反向操作。

  func main()
  a := int32(10)
  valueA := reflect.ValueOf(a)
  fmt.Printf("ValueOf(a):%v\\n", valueA)
  fmt.Printf("Interface():%v\\n", valueA.Interface())
  ai, ok := valueA.Interface().(int32)
  fmt.Printf("ok:%v val:%v\\n", ok, ai)
 

代码输出如下

  $ go run main.go
  ValueOf(a):10
  Interface():10
  ok:true val:10

 

4.3 通过反射修改对象,该对象值必须是可修改的

reflect提供func (v Value) CanSet() bool判断对象值是否修改,通过func (v Value) Set(x Value)修改对象值

  func main()
  a := int32(10)
  valueA := reflect.ValueOf(a)
  fmt.Printf("valueA :%v\\n", valueA.CanSet())
  b := int32(100)
  valuePtrB := reflect.ValueOf(&b)
  fmt.Printf("valuePtrB:%v Elem:%v\\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())
  valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))
  fmt.Printf("b:%v Elem:%v\\n", b, valuePtrB.Elem())
 

 代码输出如下

  $ go run main.go
  valueA :false
  valuePtrB:false Elem:true
  b:200 Elem:200

后续章节再分享通过修改各种类型的值的实操。

 

5. 参考文档

laws-of-reflection

interface

golang之反射

反射基本介绍

  1)反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)

  2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段,方法)

  3)通过反射,可以修改变量的值,可以调用关联的方法

  4)使用反射,需要 import ("reflect")

反射重要的函数和概念

  1)reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型

  2)reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型,reflect.Value是一个结构体类型。通过reflect.Value,可以获取到关于该变量的很多信息

  3)变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。

    interface{}  ——>  reflect.Value:

      rVal := reflect.ValueOf(b)

    reflect.Value  ——>  interface{}:

      iVal := rVal.Interface()

    interface{}  ——>  原来的变量(类型断言):

      v := iVal.(Stu)

以上是关于Golang反射获取变量类型和值的主要内容,如果未能解决你的问题,请参考以下文章

golang反射

GO开发[六]:golang反射(reflect)

golang之反射

(四十五)golang--反射

Golang basic_leaming反射

Golang basic_leaming反射