copier 源码分析
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
}