go 实现类似 spring BeanUtil 工具
作者:Z.K
- 2022-12-06 广东
本文字数:1516 字
阅读完需:约 5 分钟
纯递归实现深拷贝,src 和 dst 必须是结构体指针。
func CopyTo(src any, dst any) error {
srcPtrTyp := reflect.TypeOf(src)
if srcPtrTyp.Kind() != reflect.Pointer {
return newErrTypeError(srcPtrTyp)
}
srcTyp := srcPtrTyp.Elem()
if srcTyp.Kind() != reflect.Struct {
return newErrTypeError(srcTyp)
}
dstPtrTyp := reflect.TypeOf(dst)
if dstPtrTyp.Kind() != reflect.Pointer {
return newErrTypeError(dstPtrTyp)
}
dstTyp := dstPtrTyp.Elem()
if dstTyp.Kind() != reflect.Struct {
return newErrTypeError(dstTyp)
}
srcValue := reflect.ValueOf(src).Elem()
dstValue := reflect.ValueOf(dst).Elem()
return copyStruct(srcTyp, srcValue, dstTyp, dstValue)
}
func copyStruct(srcTyp reflect.Type, srcValue reflect.Value, dstTyp reflect.Type, dstValue reflect.Value) error {
srcFieldNameIndex := make(map[string]int, 0)
for i := 0; i < srcTyp.NumField(); i++ {
fTyp := srcTyp.Field(i)
if !fTyp.IsExported() {
continue
}
srcFieldNameIndex[fTyp.Name] = i
}
for i := 0; i < dstTyp.NumField(); i++ {
fTyp := dstTyp.Field(i)
if !fTyp.IsExported() {
continue
}
if idx, ok := srcFieldNameIndex[fTyp.Name]; ok {
if err := copyStructField(srcTyp, srcValue, dstTyp, dstValue, idx, i); err != nil {
return err
}
}
}
return nil
}
func copyStructField(
srcTyp reflect.Type,
srcValue reflect.Value,
dstTyp reflect.Type,
dstValue reflect.Value,
srcFieldIndex int,
dstFieldIndex int) error {
srcFieldType := srcTyp.Field(srcFieldIndex)
dstFieldType := dstTyp.Field(dstFieldIndex)
if srcFieldType.Type.Kind() != dstFieldType.Type.Kind() {
return newErrKindNotMatchError(srcFieldType.Type.Kind(), dstFieldType.Type.Kind(), srcFieldType.Name)
}
srcFieldValue := srcValue.Field(srcFieldIndex)
dstFieldValue := dstValue.Field(dstFieldIndex)
if srcFieldType.Type.Kind() == reflect.Pointer {
if srcFieldValue.IsNil() {
return nil
}
if dstFieldValue.IsNil() {
//设置dstFieldType指向类型的零值
dstFieldValue.Set(reflect.New(dstFieldType.Type.Elem()))
}
return copyData(srcFieldType.Type.Elem(), srcFieldValue.Elem(), dstFieldType.Type.Elem(), dstFieldValue.Elem(), srcFieldType.Name)
}
return copyData(srcFieldType.Type, srcFieldValue, dstFieldType.Type, dstFieldValue, srcFieldType.Name)
}
func copyData(
srcTyp reflect.Type,
srcValue reflect.Value,
dstTyp reflect.Type,
dstValue reflect.Value,
fieldName string,
) error {
if srcTyp.Kind() == reflect.Pointer {
return newErrMultiPointer(fieldName)
}
if srcTyp.Kind() != dstTyp.Kind() {
return newErrKindNotMatchError(srcTyp.Kind(), dstTyp.Kind(), fieldName)
}
if isShadowCopyType(srcTyp.Kind()) {
// 内置类型,但不匹配,如别名、map和slice
if srcTyp != dstTyp {
return newErrTypeNotMatchError(srcTyp, dstTyp, fieldName)
}
if dstValue.CanSet() {
//完成内置类型字段拷贝
dstValue.Set(srcValue)
}
} else if srcTyp.Kind() == reflect.Struct {
return copyStruct(srcTyp, srcValue, dstTyp, dstValue)
}
return nil
}
复制代码
实现这样一个工具,需要注意的点:
纯反射实现,可能存在性能问题
只能复制公共字段
划线
评论
复制
发布于: 刚刚阅读数: 7
Z.K
关注
还未添加个人签名 2020-07-09 加入
还未添加个人简介
评论