golang实现copy

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
}
Golang,可以使用标准库的io包的Copy函数来实现文件的copy操作。Copy函数的定义如下: ```go func Copy(dst Writer, src Reader) (written int64, err error) ``` 其,dst是目标文件的写入器,src是源文件的读取器。该函数会将源文件的内容复制到目标文件,并返回复制的字节数和可能出现的错误。\[1\] 除了使用Copy函数,还可以通过序列化和反序列化来实现对象的深度拷贝。可以使用encoding/gob包来进行序列化和反序列化操作。下面是一个基于序列化和反序列化的深度拷贝函数的示例: ```go import "encoding/gob" func deepCopy(dst, src interface{}) error { var buf bytes.Buffer if err := gob.NewEncoder(&buf).Encode(src); err != nil { return err } return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) } ``` 该函数接受两个参数,dst是目标对象,src是源对象。它会将源对象进行序列化,然后再进行反序列化,最终将结果赋值给目标对象,实现深度拷贝。\[2\] 在Golang,文件的copy、读取和写入是非常基础和常用的操作。除了使用Copy函数外,还可以使用os包和bufio包等工具来进行文件操作。在进行文件操作时,需要注意正确地打开和关闭文件句柄,以避免资源泄漏和其他问题。\[3\] #### 引用[.reference_title] - *1* *3* [Golang基础之文件的copy与文件的读写](https://blog.csdn.net/SMILY12138/article/details/129952198)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Golang的深拷贝与浅拷贝](https://blog.csdn.net/Alen_xiaoxin/article/details/124913809)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值