小狗.webp

我们好,我是渔夫子。本号新推出「go东西箱」系列,意在给我们分享使用go语言编写的、有用的、好玩的东西。

今日给我们引荐的东西是deepcopy,一个可以对指针、接口、切片、结构体、Map都能进行深复制的东西。在Go中需要对一个变量进行复制时分浅复制和深复制。浅复制就是复制后就是无论改变新值仍是原值都对对另一个产生影响,比如切片。而深复制则是将目标值彻底复制一份,消除这种影响。

完成原理剖析:深复制的完成原理本质上是经过反射完成。经过将源对象转换成接口,再对接口经过反射判断其类型,进而进行深度复制。如下就是该包的彻底完成:

package deepcopy
import (
	"reflect"
	"time"
)
// Interface for delegating copy process to type
type Interface interface {
	DeepCopy() interface{}
}
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
func Iface(iface interface{}) interface{} {
	return Copy(iface)
}
// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}.  The returned value will need to be asserted to the
// correct type.
func Copy(src interface{}) interface{} {
	if src == nil {
		return nil
	}
	// Make the interface a reflect.Value
	original := reflect.ValueOf(src)
	// Make a copy of the same type as the original.
	cpy := reflect.New(original.Type()).Elem()
	// Recursively copy the original.
	copyRecursive(original, cpy)
	// Return the copy as an interface.
	return cpy.Interface()
}
// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
	// check for implement deepcopy.Interface
	if original.CanInterface() {
		if copier, ok := original.Interface().(Interface); ok {
			cpy.Set(reflect.ValueOf(copier.DeepCopy()))
			return
		}
	}
	// handle according to original's Kind
	switch original.Kind() {
	case reflect.Ptr:
		// Get the actual value being pointed to.
		originalValue := original.Elem()
		// if  it isn't valid, return.
		if !originalValue.IsValid() {
			return
		}
		cpy.Set(reflect.New(originalValue.Type()))
		copyRecursive(originalValue, cpy.Elem())
	case reflect.Interface:
		// If this is a nil, don't do anything
		if original.IsNil() {
			return
		}
		// Get the value for the interface, not the pointer.
		originalValue := original.Elem()
		// Get the value by calling Elem().
		copyValue := reflect.New(originalValue.Type()).Elem()
		copyRecursive(originalValue, copyValue)
		cpy.Set(copyValue)
	case reflect.Struct:
		t, ok := original.Interface().(time.Time)
		if ok {
			cpy.Set(reflect.ValueOf(t))
			return
		}
		// Go through each field of the struct and copy it.
		for i := 0; i < original.NumField(); i++ {
			// The Type's StructField for a given field is checked to see if StructField.PkgPath
			// is set to determine if the field is exported or not because CanSet() returns false
			// for settable fields.  I'm not sure why.  -mohae
			if original.Type().Field(i).PkgPath != "" {
				continue
			}
			copyRecursive(original.Field(i), cpy.Field(i))
		}
	case reflect.Slice:
		if original.IsNil() {
			return
		}
		// Make a new slice and copy each element.
		cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
		for i := 0; i < original.Len(); i++ {
			copyRecursive(original.Index(i), cpy.Index(i))
		}
	case reflect.Map:
		if original.IsNil() {
			return
		}
		cpy.Set(reflect.MakeMap(original.Type()))
		for _, key := range original.MapKeys() {
			originalValue := original.MapIndex(key)
			copyValue := reflect.New(originalValue.Type()).Elem()
			copyRecursive(originalValue, copyValue)
			copyKey := Copy(key.Interface())
			cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
		}
	default:
		cpy.Set(original)
	}
}

基本使用

  • 复制切片
	a := []int{1,2,3}
	dst := deepcopy.Copy(a)
	a1 := dst.([]int)
	a1[0] = 2
	fmt.Println(a, a1) //a:[1 2 3] a1:[2 2 3]
  • 复制map
	a := make(map[string]int)
	a["k1"] = 1
	a["k2"] = 2
	a["k3"] = 3
	dst := deepcopy.Copy(a)
	a1 := dst.(map[string]int)
	a1["k1"] = 10
	fmt.Println(a, a1) //a:map[k1:1 k2:2 k3:3] a1:map[k1:10 k2:2 k3:3]

更多项目详情请查看如下链接。

开源项目地址:github.com/mohae/deepc…

开源项目作者:mohae

—特别引荐—

特别引荐:一个专心go项目实战、项目中踩坑经验及避坑指南、各种好玩的go东西的大众号,「Go学堂」,专心有用性,十分值得我们重视。点击下方大众号卡片,直接重视。重视送《100个go常见的过错》pdf文档。