首页 > 其他分享 >玩转Go反射

玩转Go反射

时间:2022-10-10 20:01:34浏览次数:47  
标签:ini 反射 err val reflect 玩转 myInit Go tpField

反射

反射常用于各种框架类当中,它可以做到十分优雅的帮我们读取值、设置值
Go语言当中感觉很多反射的工具类,比如Java中的hutool,并没有很好的支持

我总结下来反射可以分为两块,一块是TypeOf,一块是ValueOf.
TypeOf可以做到获取类型,名字,别名等,ValueOf用来获取值,设置值
由于我想去读取ini文件中的属性去设置到我的struct里面,我觉得反射处理的太过于麻烦,得一个个的去判断值是什么类型,再去把值设置进去,所以我选用了go-ini这个包,但是也不能无脑使用,所以取读一下其中的核心源码。

我是怎么使用它的

type MyInit struct {
	UserName string `ini:"username"`
	Password string `ini:"password"`
}

func main() {
	config, err := ini.Load("myInit.ini") //读取出文件成一个ini.file对象
	if err != nil { // 如果没出问题
		log.Fatalln("Fail to read file: ", err)
	}
	fmt.Println(config.Section("myInit").Key("username").String()) // 试试功能

	myInit := MyInit{} // 初始化一下接收的struct
	v := reflect.ValueOf(&myInit) // 尝试获取一下反射值
	v = v.Elem() // v是指针,把它变成值
	t := v.Type() // 对应的typeOf
	config.Section("myInit").MapTo(&myInit) // 重点使用,这里注意一定要传递&指针
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		fieldTag := t.Field(i).Tag.Get("ini")
		config.Section("myInit").Key(fieldTag).Value()
		fmt.Println(field, fieldTag)
	}
	fmt.Println(myInit)
}

源码部分

源码的逻辑,把这个config.Section("myInit")
传递进去的&myInit其实是接受一个空接口,所以要做类型判断

// MapTo maps section to given struct.
func (s *Section) MapTo(v interface{}) error {
	return s.mapTo(v, false)
}

// mapTo maps a section to object v.
func (s *Section) mapTo(v interface{}, isStrict bool) error {
	typ := reflect.TypeOf(v) // 获取type和value
	val := reflect.ValueOf(v)
	if typ.Kind() == reflect.Ptr { // 这里就是要注意,一定是要传进来指针,不然的话值传递反射根本没意义
		typ = typ.Elem() // 获取真正的值
		val = val.Elem() // 获取真正的值
	} else {
		return errors.New("not a pointer to a struct")
	}

	if typ.Kind() == reflect.Slice { // 如果是slice使用一个逻辑
		newField, err := s.mapToSlice(s.name, val, isStrict)
		if err != nil {
			return err
		}
		// 这里是直接使用了valueof反射对象的set方法,直接set了一个value值,到时候接着看源码吧
		val.Set(newField)
		return nil
	}
	// 其他都使用同样的逻辑
	return s.mapToField(val, isStrict, 0, s.name)
}

mapToField的逻辑

func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}
	typ := val.Type()

	// 这里是循环处理结构体中的每一个field
	for i := 0; i < typ.NumField(); i++ {
		field := val.Field(i)
		tpField := typ.Field(i)
		// 处理别名,在结构体中都有设置`ini:xxx`
		tag := tpField.Tag.Get("ini")
		if tag == "-" {
			continue
		}

		rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
		fieldName := s.parseFieldName(tpField.Name, rawName)
		if len(fieldName) == 0 || !field.CanSet() {
			continue
		}
		// 一些特别的判断,是否是结构体,是否是结构体指针,是否为匿名指针
		isStruct := tpField.Type.Kind() == reflect.Struct
		isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
		isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
		if isAnonymousPtr {
			// 如果是匿名指针直接就设置进去,匿名指针的意思是它是嵌入进去的,不是本身自带
			field.Set(reflect.New(tpField.Type.Elem()))
		}
		// 这里主要还是区分出是不是嵌入指针,或者是结构体,那就要单独去处理
		if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
			if isStructPtr && field.IsNil() {
				field.Set(reflect.New(tpField.Type.Elem()))
			}
			fieldSection := s
			if rawName != "" {
				sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
				if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
					fieldSection = secs[sectionIndex]
				}
			}
			if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
				return fmt.Errorf("map to field %q: %v", fieldName, err)
			}
		} else if isAnonymousPtr || isStruct || isStructPtr {
			if secs, err := s.f.SectionsByName(fieldName); err == nil {
				if len(secs) <= sectionIndex {
					return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
				}
				// Only set the field to non-nil struct value if we have a section for it.
				// Otherwise, we end up with a non-nil struct ptr even though there is no data.
				if isStructPtr && field.IsNil() {
					field.Set(reflect.New(tpField.Type.Elem()))
				}
				if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
					return fmt.Errorf("map to field %q: %v", fieldName, err)
				}
				continue
			}
		}

		// Map non-unique sections
		if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
			newField, err := s.mapToSlice(fieldName, field, isStrict)
			if err != nil {
				return fmt.Errorf("map to slice %q: %v", fieldName, err)
			}

			field.Set(newField)
			continue
		}

		if key, err := s.GetKey(fieldName); err == nil {
			delim := parseDelim(tpField.Tag.Get("delim"))
			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
				return fmt.Errorf("set field %q: %v", fieldName, err)
			}
		}
	}
	return nil
}

标签:ini,反射,err,val,reflect,玩转,myInit,Go,tpField
From: https://www.cnblogs.com/azxx/p/16776966.html

相关文章

  • SIT221 Data Structures and Algorithms课程辅导task1.1
    文章目录​​1.如何使用vector​​​​2.代码结构分析​​​​2.1类和对象​​​​2.2和原版本对比查看差距​​实现一个vector。1.如何使用vector2.代码结构分析总体......
  • Reflection 反射
    Reflection反射反射的基本概念反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法Class<?>aClass=Class.forName("ming......
  • 30 Django分页组件
    pager.py:"""如果想要使用分页,需要以下两个步骤defxxx():queryset=models.Customer.objects.filter(active=1).select_related('level','creator')#select......
  • go查看某段ip是否通的脚本
    go查看某段ip是否通的脚本例1:packagemainimport("fmt""golang.org/x/text/encoding/simplifiedchinese""os/exec""strings""sync")funcping......
  • Go_Channel详解
    一channel介绍单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。虽然可以使用共享内存进行数据交换,但是共享内存在不同的gorou......
  • Go_IO操作
    1.输入输出的底层原理终端其实是一个文件,相关实例如下:os.Stdin:标准输入的文件实例,类型为*Fileos.Stdout:标准输出的文件实例,类型为*Fileos.Stderr:标准错误输出的文件实......
  • 37、linux下安装python3.6和django
    37.1、安装python:1、python介绍:python是一种面向对象的,解释型的计算机语言,它的特点是语法简介,优雅,简单易学。1989年诞生,Guido(龟叔)开发。编译型语言:代码在编译之后,编译成......
  • 22、面向对象(反射)
    22.1、反射介绍:1、什么是反射:(1)反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机......
  • Go_Goroutine详解
    Goroutine详解goroutine的概念类似于线程,但goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将goroutine中的任务合理地分配给每个CPU。Go语言之所以被称......
  • 49、django工程(cookie+session)
    49.1、介绍:1、cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。cookie的工作原理是,由服务器产......