Go克隆几种方式
序列化的方式实现深度拷贝
最简单的方式是基于序列化和反序列化来实现对象的深度复制:
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) }
测试用例
import ( "bytes" "encoding/gob" "encoding/json" "fmt" "log" "reflect" "testing" "github.com/mohae/deepcopy" ) type Basics struct { A string B int C []string } func TestClone1(t *testing.T) { var src2 = &Basics{ A: "hello world", B: 34, C: []string{"1213", "1312"}, } dst := new(Basics) if err := deepCopy(dst, src2); err != nil { log.Fatal(err) } log.Printf("%+v", dst) }
执行结果:
=== RUN TestClone1 2021/12/23 10:37:56 &{A:hello world B:34 C:[1213 1312]} --- PASS: TestClone1 (0.00s) PASS
反射实现深度拷贝
深度复制可以基于reflect包的反射机制完成, 但是全部重头手写的话会很繁琐.
/深度克隆,可以克隆任意数据类型 func DeepClone(src interface{}) (interface{}, error) { typ := reflect.TypeOf(src) if typ.Kind() == reflect.Ptr { typ = typ.Elem() dst := reflect.New(typ).Elem() b, _ := json.Marshal(src) if err := json.Unmarshal(b, dst.Addr().Interface()); err != nil { return nil, err } return dst.Addr().Interface(), nil } else { dst := reflect.New(typ).Elem() b, _ := json.Marshal(src) if err := json.Unmarshal(b, dst.Addr().Interface()); err != nil { return nil, err } return dst.Interface(), nil } }
测试用例
import ( "bytes" "encoding/gob" "encoding/json" "fmt" "log" "reflect" "testing" "github.com/mohae/deepcopy" ) type Basics struct { A string B int C []string } func TestDeepClone(t *testing.T) { var src = &Basics{ A: "hello world", B: 34, C: []string{"1213", "1312"}, } dst, _ := DeepClone(src) fmt.Println(dst) }
执行结果:
=== RUN TestDeepClone &{hello world 34 [1213 1312]} --- PASS: TestDeepClone (0.00s) PASS
借助包深拷贝
"github.com/mohae/deepcopy"
使用方式如下:
dst := deepcopy.Copy(src)
测试用例
import "github.com/mohae/deepcopy" type BasicsSmall struct { a string b int c []string } func TestDeepCopy(t *testing.T) { //src := &User{Name: "xiaomng", Age: 100} var src = &BasicsSmall{ a: "hello world", b: 34, c: []string{"1213", "1312"}, } dst := deepcopy.Copy(src) fmt.Println(dst) }
测试结果:
=== RUN TestDeepCopy &{ 0 []} --- PASS: TestDeepCopy (0.00s) PASS
为啥会出现上面的结果,因为 struct 结构,首字母都是小写的!!!
换个用例
import "github.com/mohae/deepcopy" type User struct { Name string // 小写变量,不能被deepCopy函数拷贝成功 Age int } func TestDeepCopy(t *testing.T) { src := &User{Name: "xiaomng", Age: 100} //var src = &BasicsSmall{ // a: "hello world", // b: 34, // c: []string{"1213", "1312"}, //} dst := deepcopy.Copy(src) fmt.Println(dst) }
执行结果:
=== RUN TestDeepCopy &{xiaomng 100} --- PASS: TestDeepCopy (0.00s) PASS
总结
上述拷贝有个问题,结构体中有小写成员变量时,上述方式无效。