Go语言通过反射获取各种类型变量的值

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的反

向操作。

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) 修改对象值。

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())
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值