go reflect常见应用
Posted 文大侠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go reflect常见应用相关的知识,希望对你有一定的参考价值。
目录
借助golang reflect可以使程序更加灵活和简洁,本文介绍常用的两个场景,抛砖引玉,供参考。
1. 不同类型填充
常见的我们会遇到只关心结构不关心类型的场景,比如常见的函数输入指定的类型来操作。那么能否实现这样的功能,输入不同的结构体,但是它们的字段类型和名称基本一致,使用同一个函数来填充?
如下,使用reflect可实现这一通用fill函数,Person和Age类型不一样,但是通过反射可以做到相同函数填充。
// 万能程序,处理填充结构体字段
type Person struct
Name string
Age int
type Animal struct
Name string
Age int
func fillBySetting(o interface, fields map[string]interface) error
t := reflect.TypeOf(o)
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct
return errors.New("Must input a pointer to struct")
v := reflect.ValueOf(o)
for field, value := range fields
if ft, ok := v.Elem().Type().FieldByName(field); !ok
continue // 对应待设置字段不存在
else if ft.Type == reflect.TypeOf(value) // 类型一致时才设置
v.Elem().FieldByName(field).Set(reflect.ValueOf(value))
return nil
func TestFill(t *testing.T)
p := &Person
a := &Animal
fillBySetting(p, map[string]interface"Name": "wenzhou", "Age": 19)
fillBySetting(a, map[string]interface"Name": "zhuzhu", "Age": 18)
t.Log(p)
t.Log(a)
2. 结构体bind
类似json解析,具体流程其实和上述fill一致,
1.通过反射遍历字段
2.按照每个字段tag去取的填充信息中取值
3.校验下取值类型
4.填充
具体代码如下
func TestBind(t *testing.T)
value := map[string]string
"int_value": "102",
"string_value": "old_man",
"int_array": "[1,2,3]",
"string_array": "[\\"1\\",\\"2\\",\\"3\\"]",
var result BindStruct
t.Log(result)
err := BindStructWithMap(value, &result)
if err != nil
t.Log("绑定错误", err)
t.Log(result)
func BindStructWithMap(configMap map[string]string, result interface) error
BindValue := func(field reflect.Value, tfield reflect.StructField, value string) error
switch field.Kind()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
res, err := strconv.ParseInt(value, 10, 64)
if err != nil
return err
field.SetInt(res)
case reflect.String:
field.SetString(value)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
res, err := strconv.ParseUint(value, 10, 64)
if err != nil
return err
field.SetUint(res)
case reflect.Float32:
res, err := strconv.ParseFloat(value, 32)
if err != nil
return err
field.SetFloat(res)
case reflect.Float64:
res, err := strconv.ParseFloat(value, 64)
if err != nil
return err
field.SetFloat(res)
case reflect.Slice:
// 或取数组元素类型和type信息
elemKind := tfield.Type.Elem().Kind()
elemType := tfield.Type.Elem()
fmt.Println(elemKind) // int
fmt.Println(elemType) // int
// 对应的配置数组也切分,然后遍历将结果配置数组逐一映射到结果集合
// strArray--》valArray 一一映射
value = strings.Trim(strings.Trim(value, "["), "]")
strArray := strings.Split(value, ",")
var valArray []reflect.Value
switch elemKind
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
for _, e := range strArray
ee, err := strconv.ParseInt(e, 10, 64)
if err != nil
return err
valArray = append(valArray, reflect.ValueOf(ee).Convert(elemType))
case reflect.String:
for _, e := range strArray
valArray = append(valArray, reflect.ValueOf(strings.Trim(e, "\\"")).Convert(elemType))
// 重新设置值
valArr := reflect.Append(field, valArray...)
field.Set(valArr)
return nil
// 被绑定的结构体非指针错误返回
if reflect.ValueOf(result).Kind() != reflect.Ptr
errors.New("input must be a pointer to struct")
// 被绑定的结构体指针不能为 null
if reflect.ValueOf(result).IsNil()
errors.New("input pointer must not be nil")
// 获取结果反射信息
v := reflect.ValueOf(result).Elem()
t := v.Type()
// 遍历struct 字段 在已有的map值中查找是否存在,存在则如下赋值
//v.Field(i).SetInt(res)
//v.Field(i).SetUint(res)
//v.Field(i).SetFloat(res)
for i := 0; i < t.NumField(); i++
tfield := t.Field(i)
field := v.Field(i)
tag := tfield.Tag.Get("bingkey")
fmt.Println(tag)
// map 中没该变量有则跳过
value, ok := configMap[tag]
if !ok
continue
// 绑定上
err := BindValue(field, tfield, value)
if nil != err
return err
return nil
可以看到,借助reflect,使程序可以简洁很多,但是注意reflect相对性能较低,对于性能较高的场合要不不适用reflect要不第一次取reflect信息后缓存使用。
参考 https://zhuanlan.zhihu.com/p/136371822
原创,转载请注明来自
以上是关于go reflect常见应用的主要内容,如果未能解决你的问题,请参考以下文章