使用反射可以获取到数据的类型信息,reflect.TypeOf()返回的数据类型如下:
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
这里我们常用到的是size(大小)、kind(底层类型名称)。
对于结构体,我们可以使用Field(i)方法获取到结构体中每个元素的信息,返回的数据类型如下:
type StructField struct {
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
这里我们常用到的是Type(类型信息结构体)、Offset(偏移量)。
对于数组和切片,我们可以使用Elem()方法获取到每个元素的类型,返回的数据为Type类型,因为数组中的每个元素的类型都是一样的,所以偏移量也都一致,使用Size()方法获取即可。
通常我们使用unsafe.Pointer来获取某个数据的地址,但是unsafe.Pointer是无法进行计算的,它只是个单纯的指针类型,因此无法计算偏移量,这时候就需要使用uintptr了。因为uintptr底层是个整形,所以可以直接用来计算偏移量,而且unsafe.Pointer与uintptr是可以相互转换的。
有了上面这些工具,那么就可以愉快地获取数据类型的信息了。
样例:
type Game struct {
name string
gameType string
price int
}
func main() {
//Obj := []int{11,12,13,14,15,16}
//Obj := []byte("我朋友需要268块做手术.jpg")
Obj := Game{
name: "条狗:无限影逝",
gameType: "ACT",
price: 268,
}
ObjectPtr := unsafe.Pointer(&Obj) //获取obj起始位置
ObjectType := reflect.TypeOf(Obj) //获取obj类型
switch ObjectType.Kind() {
case reflect.Struct:
{
for i := 0; i < ObjectType.NumField(); i++ { //遍历结构体内所有元素
fd := ObjectType.Field(i)
switch fd.Type.Kind() { //获取当前元素的类型
case reflect.String:
fmt.Println(*(*string)(unsafe.Pointer(uintptr(ObjectPtr) + uintptr(fd.Offset)))) //计算偏移量
case reflect.Int:
fmt.Println(*(*int)(unsafe.Pointer(uintptr(ObjectPtr) + uintptr(fd.Offset))))
//判断其他Kind
}
}
}
case reflect.Slice:
{
SlicePtr := (*reflect.SliceHeader)(ObjectPtr)
fieldPtr := unsafe.Pointer(SlicePtr.Data)
ElemType := ObjectType.Elem() //int
for i := 0; i < SlicePtr.Len; i++ {
switch ElemType.Kind() {
case reflect.Int:
fmt.Println(*(*int)(unsafe.Pointer(uintptr(fieldPtr) + uintptr(i*int(ElemType.Size())))))
case reflect.Uint8:
fmt.Println(*(*uint8)(unsafe.Pointer(uintptr(fieldPtr) + uintptr(i*int(ElemType.Size())))))
//判断其他Kind
}
}
}
//判断其他Kind
}
}