Golang 使用reflect与Pointer获取传入的interface信息

使用反射可以获取到数据的类型信息,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
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值