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 mainimport ("fmt""reflect"
)func main() {type MyInt32 int32a := MyInt32(1)b := int32(1)// reflect.TypeOf(a):main.MyInt32 Kind:int32fmt.Printf("reflect.TypeOf(a):%v Kind:%v\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())// reflect.TypeOf(b):int32 Kind:int32fmt.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 uintconst (Invalid Kind = iotaBoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128ArrayChanFuncInterfaceMapPointerSliceStringStructUnsafePointer
)
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 mainimport ("fmt""reflect"
)type myStruct struct {
}func main() {a := &myStruct{}typeA := reflect.TypeOf(a)// TypeOf(a):*main.myStruct Kind:ptrfmt.Printf("TypeOf(a):%v Kind:%v\n", typeA, typeA.Kind())// TypeOf(a).Elem():main.myStruct Elem().Kind:structfmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\n", typeA.Elem(), typeA.Elem().Kind())s := []int64{}typeS := reflect.TypeOf(s)// TypeOf(s):[]int64 Kind:slicefmt.Printf("TypeOf(s):%v Kind:%v\n", typeS, typeS.Kind())// TypeOf(s).Elem():int64 Elem().Kind:int64fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\n", typeS.Elem(), typeS.Elem().Kind())
}
从代码输出可以看出,通过 reflect.Elem() 函数可以获取引用指向数据的类型。
2.3 结构体成员类型
通过 NumField 获取成员数量,Field 通过下标访问成员的类型信息 StructField,包括成员名称、类型、Tag 信息
等。
package mainimport ("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:3fmt.Printf("NumField:%v \n", typeS.NumField())// 每个成员的信息 包括名称、类型、Tagfor 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_jsonfmt.Printf("json tag val:%+v\n", field.Tag.Get("json"))if value, ok := field.Tag.Lookup("orm"); ok {// rm tag val:column:num_ormfmt.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 mainimport ("fmt""reflect"
)func main() {b := int32(1)valueB := reflect.ValueOf(b)// reflect.TypeOf(b):1 Kind:int32fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", valueB, valueB.Kind())s := "abcdefg"valueS := reflect.ValueOf(s)// reflect.TypeOf(s):abcdefg Kind:string Len:7fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
}
3.1 结构体的成员的值
和 2.3 结构体成员类型获取结构体成员类型类似,reflect 提供了 NumField 获取成员数量,Field 通过下标访问成
员的值。
package mainimport ("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:3fmt.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:100fmt.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 mainimport ("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:6fmt.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):6fmt.Printf("valueS.Index(%v):%v\n", i, valueS.Index(i))}
}
3.3 遍历map
reflect 有两种方法遍历 map
-
通过迭代器 MapIter 遍历 map
-
先获取 map 的所有 key,再通过 key 获取对应的 value
package mainimport ("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:3fmt.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:3fmt.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 mainimport ("fmt""reflect"
)func main() {a := int32(10)valueA := reflect.ValueOf(a)// ValueOf(a):10fmt.Printf("ValueOf(a):%v\n", valueA)// Interface():10fmt.Printf("Interface():%v\n", valueA.Interface())ai, ok := valueA.Interface().(int32)// ok:true val:10fmt.Printf("ok:%v val:%v\n", ok, ai)
}
4.3 通过反射修改对象,该对象值必须是可修改的
reflect提供 func (v Value) CanSet() bool 判断对象值是否修改,通过 func (v Value) Set(x Value) 修改对象值。
package mainimport ("fmt""reflect"
)func main() {a := int32(10)valueA := reflect.ValueOf(a)// valueA :falsefmt.Printf("valueA :%v\n", valueA.CanSet())b := int32(100)valuePtrB := reflect.ValueOf(&b)// valuePtrB:false Elem:truefmt.Printf("valuePtrB:%v Elem:%v\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))// b:200 Elem:200fmt.Printf("b:%v Elem:%v\n", b, valuePtrB.Elem())
}