golang 没有发现太好的 struct 深拷贝的方法,因此自己写了一个深拷贝的代码,可以满足大多数的深拷贝场景
import (
"context"
"reflect"
"time"
)
// Copy copies the values of all exported fields, dst and src must be pointers
func Copy(ctx context.Context, src, dst interface{}) bool {
return CopyIgnore1(ctx, src, dst, nil)
}
func CopyIgnore1(ctx context.Context, src, dst interface{}, fieldRender map[string]string) bool {
return CopyIgnore2(ctx, src, dst, fieldRender, nil)
}
func CopyIgnore2(ctx context.Context, src, dst interface{}, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src == nil || dst == nil {
return false
}
dstValue := reflect.ValueOf(dst).Elem()
srcValue := reflect.ValueOf(src).Elem()
return doCopy(ctx, srcValue, dstValue, fieldRender, ignoreType)
}
func doCopy(ctx context.Context, srcValue, dstValue reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
switch dstValue.Kind() {
case reflect.Ptr:
return copyPtr(ctx, srcValue, dstValue, fieldRender, ignoreType)
case reflect.Slice:
return copySlice(ctx, srcValue, dstValue, fieldRender, ignoreType)
case reflect.Map:
return copyMap(ctx, srcValue, dstValue, fieldRender, ignoreType)
case reflect.Struct:
return copyStruct(ctx, srcValue, dstValue, fieldRender, ignoreType)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Bool, reflect.String:
return copyBase(ctx, srcValue, dstValue, fieldRender, ignoreType)
case reflect.Interface:
return copyInterface(ctx, srcValue, dstValue, fieldRender, ignoreType)
default:
return false
}
}
func copyInterface(ctx context.Context, src reflect.Value, dst reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
elem := newElemByType(src.Type()).Elem()
if doCopy(ctx, src, elem, fieldRender, ignoreType) {
dst.Set(elem)
return true
}
return false
}
func copyPtr(ctx context.Context, src, dest reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src.Kind() != reflect.Ptr {
return false
}
if src.IsNil() {
return false
}
elem := newElem(dest)
if doCopy(ctx, src.Elem(), elem.Elem(), fieldRender, ignoreType) {
dest.Set(elem)
return true
}
return false
}
func copyStruct(ctx context.Context, src, dest reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src.Kind() != reflect.Struct {
return false
}
for _, it := range ignoreType {
if src.Type() == it {
return false
}
}
t, ok := src.Interface().(time.Time)
if ok {
dest.Set(reflect.ValueOf(t))
return false
}
var copyIndexList []int
for i := 0; i < src.NumField(); i++ {
srcField := src.Field(i)
srcTypeField := src.Type().Field(i)
if !srcTypeField.IsExported() {
continue
}
srcName := srcTypeField.Name
dstName := renderFieldName(ctx, fieldRender, srcName)
if dstName == "" {
continue
}
dstFieldByName := dest.FieldByName(srcName)
dstTypeField, isExist := dest.Type().FieldByName(srcName)
if !isExist || !dstTypeField.IsExported() {
continue
}
if dstFieldByName.IsValid() {
if doCopy(ctx, srcField, dstFieldByName, fieldRender, ignoreType) {
copyIndexList = append(copyIndexList, i)
}
}
}
if len(copyIndexList) > 0 {
return true
}
return false
}
func renderFieldName(ctx context.Context, render map[string]string, srcName string) string {
if v, ok := render[srcName]; ok {
return v
}
return srcName
}
func copySlice(ctx context.Context, src, dest reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src.Kind() != reflect.Slice {
return false
}
if src.IsNil() {
return false
}
var copyIndexList []int
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
for i := 0; i < src.Len(); i++ {
elem := newElem(dest.Index(i))
if doCopy(ctx, extractElem(src.Index(i)), elem.Elem(), fieldRender, ignoreType) {
dest.Index(i).Set(extractElemByValue(dest.Index(i), elem))
copyIndexList = append(copyIndexList, i)
}
}
if len(copyIndexList) > 0 {
return true
}
return false
}
func copyMap(ctx context.Context, src, dest reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src.Kind() != reflect.Map {
return false
}
if src.IsNil() {
return false
}
if src.Type().Key() != dest.Type().Key() {
return false
}
if dest.Type().Elem() != src.Type().Elem() {
return false
}
var copyKeyList []any
dest.Set(reflect.MakeMap(dest.Type()))
for _, key := range src.MapKeys() {
originValue := src.MapIndex(key)
destValue := newElemByType(dest.Type().Elem())
if !doCopy(ctx, extractElem(originValue), destValue.Elem(), fieldRender, ignoreType) {
continue
}
destKey := newElemByType(dest.Type().Key())
if !doCopy(ctx, extractElem(key), destKey.Elem(), fieldRender, ignoreType) {
continue
}
copyKeyList = append(copyKeyList, key.Interface())
dest.SetMapIndex(extractElemByType(dest.Type().Key(), destKey), extractElemByType(dest.Type().Elem(), destValue))
}
if len(copyKeyList) > 0 {
return true
}
return false
}
func copyBase(ctx context.Context, src, dest reflect.Value, fieldRender map[string]string, ignoreType []reflect.Type) bool {
if src.Kind() != dest.Kind() {
return false
}
dest.Set(src)
return true
}