// deepcopy makes deep copies of things. A standard copy will copy the // pointers: deep copy copies the values pointed to. Unexported field // values are not copied. // // Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved. // License: MIT, for more details check the included LICENSE file. package deepcopy import ( "errors" "reflect" "time" // _ "github.com/stretchr/testify/assert" ) // Interface for delegating copy process to type type DeepCopyer 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. // 只支持常规类型的复制,例如:简单类型,时间,map,slice,array,指针,结构体及结构体 // 不要用于有循环引用的数据 // 多个指针指向同一个变量,复制后会生成多个 // 未导出类型不复制; 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. deep := 0 err := copyRecursive(original, cpy, deep) if err != nil { panic(err) } // 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. // 支持:简单类型,时间,map,slice,array,指针,结构体及结构体匿名变量; // 其他不支持(reflect.Chan, reflect.Func, reflect.Invalid, reflect.UnsafePointer) // 未导出类型不复制; // 多个指针指向同一个变量,复制后会生成多个 // 不要用于有循环引用的数据,深度只能防死循环,不能防重复构造; // noCopy结构未处理 // original 类型值 // cpy 新类型值 指针的解引用 func copyRecursive(original, cpy reflect.Value, deep int) error { deep += 1 if deep > 20 { return errors.New("max deep") } // check for implement deepcopy.Interface if original.CanInterface() { if copier, ok := original.Interface().(DeepCopyer); ok { cpy.Set(reflect.ValueOf(copier.DeepCopy())) return nil } } // fmt.Println(original.Type().Name(), original.Type().String()) var err error // 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 nil } cpy.Set(reflect.New(originalValue.Type())) err = copyRecursive(originalValue, cpy.Elem(), deep) case reflect.Interface: // If this is a nil, don't do anything if original.IsNil() { return nil } // Get the value for the interface, not the pointer. originalValue := original.Elem() // Get the value by calling Elem(). copyValue := reflect.New(originalValue.Type()).Elem() err = copyRecursive(originalValue, copyValue, deep) cpy.Set(copyValue) case reflect.Struct: t, ok := original.Interface().(time.Time) if ok { cpy.Set(reflect.ValueOf(t)) return nil } oriType := original.Type() if _, ok := oriType.FieldByName("noCopy"); ok { return nil } // Go through each field of the struct and copy it. num := original.NumField() for i := 0; i < num; i++ { if !oriType.Field(i).IsExported() { continue } err = copyRecursive(original.Field(i), cpy.Field(i), deep) if err != nil { break } } case reflect.Slice: if original.IsNil() { return nil } // 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++ { err = copyRecursive(original.Index(i), cpy.Index(i), deep) if err != nil { break } } case reflect.Map: if original.IsNil() { return nil } cpy.Set(reflect.MakeMap(original.Type())) for _, key := range original.MapKeys() { originalValue := original.MapIndex(key) copyValue := reflect.New(originalValue.Type()).Elem() err = copyRecursive(originalValue, copyValue, deep) if err != nil { break } copyKey := reflect.New(key.Type()).Elem() err = copyRecursive(key, copyKey, deep) if err != nil { break } cpy.SetMapIndex(copyKey, copyValue) } case reflect.Array: for i := 0; i < original.Len(); i++ { copyRecursive(original.Index(i), cpy.Index(i), deep) } case reflect.Uintptr, reflect.Chan, reflect.Func, reflect.Invalid, reflect.UnsafePointer: return nil default: cpy.Set(original) } return err }