golang的反射很慢,这个和它的api设计有关。
type_ := reflect.ValueOf(obj)
fieldValue := type_.FieldByName("hello")
这里取出来的fielValue类型是reflect.Value,它是一个具体的值,而不是一个可复用的反射对象。这样每次反射都需要malloc这个reflect.Value结构体。
Jsoniter是golang是实现的,基于反射的JSON解析器。其实原理是用reflect.Type得出来的信息来直接做反射。而不依赖于reflect.ValueOf。
具体做法
结构体
利用refect.StructField取得对象上的值,对应的代码在: go/feature_reflect_object.go at master · json-iterator/go · GitHub:
fieldPtr := uintptr(structPtr) + field.Offset
在reflect.StructField上有一个Offset的属性。利用这个可以计算出字段的指针值。测试示例:
type TestObj struct {
field1 string
}
struct_ := &TestObj{}
field, _ := reflect.TypeOf(struct_).Elem().FieldByName("field1")
field1Ptr := uintptr(unsafe.Pointer(struct_)) + field.Offset
*((*string)(unsafe.Pointer(field1Ptr))) = "hello"
fmt.Println(struct_)
打印出来的消息是&{hello}
获取interface{}的指针
如果对应的结构体是以interface{}传进来的。还需要从interface{}上取得结构体的指针
type TestObj struct {
field1 string
}
struct_ := &TestObj{}
structInter := (interface{})(struct_)
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *struct{}
word unsafe.Pointer
}
structPtr := (*emptyInterface)(unsafe.Pointer(&structInter)).word
field, _ := reflect.TypeOf(structInter).Elem().FieldByName("field1")
field1Ptr := uintptr(structPtr) + field.Offset
*((*string)(unsafe.Pointer(field1Ptr))) = "hello"
fmt.Println(struct_)
slice类型
对应的代码在:go/feature_reflect_array.go at master · json-iterator/go · GitHub
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
slice的秘密在于取出指向数组头部的指针,然后具体的元素,通过偏移量来计算。
slice := []string{"hello", "world"}
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
header := (*sliceHeader)(unsafe.Pointer(&slice))
fmt.Println(header.Len)
elementType := reflect.TypeOf(slice).Elem()
secondElementPtr := uintptr(header.Data) + elementType.Size()
*((*string)(unsafe.Pointer(secondElementPtr))) = "!!!"
fmt.Println(slice)
打印出来的内容:
2
[hello !!!]
Map
对于Map类型来说,没有reflect.ValueOf之外的获取其内容的方式。所以只能老老实实地用golang自带的值反射api。