3

Golang反射获取变量类型和值 - Amos01

 1 year ago
source link: https://www.cnblogs.com/amos01/p/16945563.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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可以理解为类型的具体分类。如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.goreflect.TypeOf(a):main.MyInt32 Kind:int32reflect.TypeOf(b):int32 Kind:int32

种类定义点击查看

// A Kind represents the specific kind of type that a Type represents.// The zero Kind is not a valid kind.type Kind uintconst ( 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)

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

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.goTypeOf(a):*main.myStruct Kind:ptrTypeOf(a).Elem():main.myStruct Elem().Kind:structTypeOf(s):[]int64 Kind:sliceTypeOf(s).Elem():int64 Elem().Kind:int64

3.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([]int{2, 0}))}

代码输出如下,

$ go run main.goNumField: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_jsonCnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false}

4. reflect.Value

通过reflect.ValueOf获取变量值、值类型,种类为Array, Chan, Map, Slice, 或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.goreflect.TypeOf(b):1 Kind:int32reflect.TypeOf(s):abcdefg Kind:string Len:7

4.2 结构体的成员的值

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{[]int64{1, 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([]int{2, 0}))}

代码输出如下

$ go run main.goNumField:3 value(0):100value(1):descvalue(2):{Cnt:[1 2 3]}FieldByName("Num") value:100Cnt field:[1 2 3]

4.3 遍历array、slice

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

func main() { s := []int64{1, 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.goValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6valueS.Index(0):1valueS.Index(1):2valueS.Index(2):3valueS.Index(3):4valueS.Index(4):5valueS.Index(5):6

4.4 遍历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.gokey:1 val:1key:2 val:2key:3 val:3------key:3 val:3key:1 val:1key:2 val:2

5. 反射的三大定律

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

  • 获取类型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)信息。

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

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.goValueOf(a):10Interface():10ok:true val:10

5.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.govalueA :falsevaluePtrB:false Elem:trueb:200 Elem:200

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

5. 参考文档

laws-of-reflection

interface


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK