go reflect常见应用

Posted 文大侠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go reflect常见应用相关的知识,希望对你有一定的参考价值。

目录

1. 不同类型填充

2. 结构体bind


借助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常见应用的主要内容,如果未能解决你的问题,请参考以下文章

go reflect常见应用

一文初探 Go reflect 包

java中反射(Reflect)的常见应用场景

go reflect详解

go reflect详解

go reflect详解