玩转Go反射

反射

反射常用于各种框架类当中,它可以做到十分优雅的帮我们读取值、设置值
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
}
posted @ 2022-10-10 19:51  azxx  阅读(19)  评论(0编辑  收藏  举报