This is a creation in Article, where the information may have evolved or changed.
Objective
Because the Golang static strongly typed language feature and the lack of good generic support lead to writing Web services with go, it always takes a lot of time to parse and type-convert the HTTP params, and it makes the code redundant, so what can I do to solve this pain? The answer is yes, I'll tell you how to use the reflect
package to write a tool class to implement the auto-map binding between the model layer struct and the HTTP params.
The implementation is very simple, the main use is to get the reflect.TypeOf()
type of the field (including the field name, type, tag description and other related information), as well as reflect.ValueOf()
to get the field value type used to replicate the data obtained from the params, but also pay attention to the different types of values at the Set
time of the difference.
Materials
First we design a struct to store the properties of each reflection field, such as the following.
Note: Depending on the Golang's implementation of the reflection model, this operation is not so efficient in go, it is recommended to cache a result to memory after the first reflection, so that it can be acquired directly at the next use.
type field struct {name stringdef booldefValue reflect.Valuerequired bool}
Through range
reflect. The Type gets the struct field information and populates []*field
it, including whether the field must be passed, the default value, or the field name, which can be implemented using the custom TAG description.
type Account struct {Email string `params:"email;required"`Name string `params:"name;required"`Sign string `params:"sign"`San int `params:"sam" defalut:"-99999"`}
Besides, we're going to need a bitmap. Used to map reflect.Kind
the corresponding type relationship so that the type conversion is done at Set Value
var bitMap = map[reflect.Kind]int{reflect.Int.Stringreflect.Int: 32,reflect.Int16: 16,reflect.Int32: 32,reflect.Int64: 64,reflect.Int8: 8,reflect.Uint: 32,reflect.Uint16: 16,reflect.Uint32: 32,reflect.Uint64: 64,reflect.Uint8: 8,reflect.Float32: 32,reflect.Float64: 64,}
Realize
When we complete the above materials, want to realize our function that is easy, just need us to range
define a good field slice, in turn from the value in the url.Values
Get parameter, because the HTTP protocol request message is text-oriented no additional type description, So the data we get are all text types, and we need to reflect.Kind
invoke different methods based on different types Set
.
func setValue(data string, v reflect.Value) (err error) {kind := v.Kind()switch kind {case reflect.Int64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:var d int64d, err = strconv.ParseInt(data, 10, bitMap[kind])if err != nil {return}v.SetInt(d)case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:var d uint64d, err = strconv.ParseUint(data, 10, bitMap[kind])if err != nil {return}v.SetUint(d)case reflect.Float64, reflect.Float32:var d float64d, err = strconv.ParseFloat(data, bitMap[kind])if err != nil {return}v.SetFloat(d)case reflect.String:if data == "" {return}v.SetString(data)return
Reflect performance optimization
Reflect slow mainly has two reasons: one is related to memory allocation malloc
after the GC (which improved in Go 1.9 vsersion), and the other is that the reflect implementation has a large number of enumerations, as well as type deduction, and the reflect.ValueOf()
resulting reflect.Value
type is a The value of the body, each reflection needs to be re- malloc
added, which slows down the whole process.
So what if we don't use the reflect.ValueOf()
data that gets the value and use reflect.TypeOf()
the result directly struct
, would it be faster to dispense with a layer of reflection? The answer, of course, is certain! directly on the code ~
func setValueWithPointer() {acc := &Account{}tp := reflect.TypeOf(acc).Elem()field, _ := tp.FieldByName("Email")fieldPtr := uintptr(unsafe.Pointer(acc)) + field.Offset*((*string)(unsafe.Pointer(fieldPtr))) = "admin#otokaze.cn"fmt.Println(acc) // stdout: &{admin#otokaze.cn 0}}
We have cleverly used the offset of the reflect.TypeOf()
internal field memory address reserved for our struct, and also because uintptr()
the strong turn is a shape memory address, which can be arithmetic operations, as long as the initialization of the struct after the allocation of the memory address of the beginning of the Storage plus field memory The offset of the address, we can directly get the field in the physical memory address, so as to write what we need. The most straightforward way to do this is to save reflect.ValueOf()
two of reflections, and also to achieve our purpose of modification.
Above, so long as mastered the correct posture, Golang the reflection efficiency can still have a great increase! Reflective scenarios are far more than that, we all know that because static language relationships in Golang do not have the same support as mutable variables in PHP, they $$
can also be reflected to achieve similar effects, but this is not the scope of this article today, it as a knowledge point, and guidance. There are more tricks waiting for you to find out ~