源码分析:copier

Written by with ♥ on in Go

copier 的源码很少,但充分体现了 go 反射的精髓。

Copy()

func Copy(toValue interface{}, fromValue interface{}) (err error) {
	var (
		isSlice bool
		amount  = 1 // 默认为 1,from 可能是个结构体
		from    = indirect(reflect.ValueOf(fromValue)) // 取出 reflect.Value 值
		to      = indirect(reflect.ValueOf(toValue)) // 取出 reflect.Value 值
	)

	if !to.CanAddr() {
		return errors.New("copy to value is unaddressable")
	}

	// Return is from value is invalid
	if !from.IsValid() {
		return
	}

  fromType := indirectType(from.Type()) // 取出类型(slice 中的元素类型)
	toType := indirectType(to.Type()) // 取出类型

	// Just set it if possible to assign
	// And need to do copy anyway if the type is struct
  // AssignableTo 判断是否类型之间可以赋值(例如 int -> int)
	if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
		to.Set(from)
		return
	}
	
  // 如果 to、from 不是结构体,元素本身本身不是结构体,或者 slice 中的元素不是结构体
	if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
		return
	}
	
  // 如果写入的是 slice,根据来源 slice 类型判断写入的长度
	if to.Kind() == reflect.Slice {
		isSlice = true
		if from.Kind() == reflect.Slice {
			amount = from.Len()
		}
	}
	
  // 循环写入
	for i := 0; i < amount; i++ {
		var dest, source reflect.Value
	
    // 如果是写入 slice
		if isSlice {
			// source
      // 取出一个源数据(可能是指针数组,使用 indirect 处理)
			if from.Kind() == reflect.Slice {
				source = indirect(from.Index(i))
			} else {
				source = indirect(from)
			}
			// dest
      // 初始化写入数据
			dest = indirect(reflect.New(toType).Elem())
		} else {
      // 此时是 struct -> struct,没有 slice -> struct 的 case,逻辑里没有判断这种 case 代码会执行到后面的 FieldByName panic
			source = indirect(from)
			dest = indirect(to)
		}

		// check source
    // 判断 Value 是非零值
		if source.IsValid() {
			fromTypeFields := deepFields(fromType)
			//fmt.Printf("%#v", fromTypeFields)
			// Copy from field to field or method
			for _, field := range fromTypeFields {
				name := field.Name

				if fromField := source.FieldByName(name); fromField.IsValid() {
					// has field
          // 判断 Field 匹配
					if toField := dest.FieldByName(name); toField.IsValid() {
						if toField.CanSet() {
							if !set(toField, fromField) {
                // 递归 Copy 每个字段
								if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
									return err
								}
							}
						}
					} else {
						// try to set to method
            // 尝试根据函数名赋值
						var toMethod reflect.Value
						if dest.CanAddr() {
							toMethod = dest.Addr().MethodByName(name)
						} else {
							toMethod = dest.MethodByName(name)
						}

						if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
							toMethod.Call([]reflect.Value{fromField})
						}
					}
				}
			}

			// Copy from method to field
			for _, field := range deepFields(toType) {
				name := field.Name

				var fromMethod reflect.Value
				if source.CanAddr() {
					fromMethod = source.Addr().MethodByName(name)
				} else {
					fromMethod = source.MethodByName(name)
				}

				if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
					if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
            // 如果是函数,调用函数获取结果(函数不能有参数)
						values := fromMethod.Call([]reflect.Value{})
						if len(values) >= 1 {
              // 设置字段
							set(toField, values[0])
						}
					}
				}
			}
		}
    // 如果是复制到 slice
		if isSlice {
			if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
        // slice 增加一个元素
				to.Set(reflect.Append(to, dest.Addr()))
			} else if dest.Type().AssignableTo(to.Type().Elem()) {
				to.Set(reflect.Append(to, dest))
			}
		}
	}
	return
}

Indirect()

func indirect(reflectValue reflect.Value) reflect.Value {
	for reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}
	return reflectValue
}

func indirectType(reflectType reflect.Type) reflect.Type {
	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
    // 如果是指针,取出直接类型
		reflectType = reflectType.Elem()
	}
	return reflectType
}

DeepFields()

func deepFields(reflectType reflect.Type) []reflect.StructField {
	var fields []reflect.StructField

	if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
		for i := 0; i < reflectType.NumField(); i++ {
			v := reflectType.Field(i)
			if v.Anonymous {
				fields = append(fields, deepFields(v.Type)...)
			} else {
				fields = append(fields, v)
			}
		}
	}

	return fields
}