Go克隆几种方式

简介: Go克隆几种方式

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


总结

上述拷贝有个问题,结构体中有小写成员变量时,上述方式无效。

相关文章
|
8月前
|
设计模式 算法 Go
Go的模板方式模式
模板方法模式是一种行为设计模式,它在一个抽象类中定义了一个算法的骨架,允许子类为算法的某些步骤提供具体实现。它促进了代码的重用,并为一组相关算法定义了一个通用的结构。
36 0
|
4月前
|
算法 Go PHP
GO 比较两个对象是否相同
GO 比较两个对象是否相同
|
5月前
|
机器学习/深度学习 Go C语言
创建第一个Go的程序Hello Kitty
创建第一个Go的程序Hello Kitty
98 7
|
9月前
|
Go 开发者
[Go开源工具] go-optioner:轻松生成函数选项模式代码
你是否使用过 functional options 函数选项模式?在使用时,你是否遇到过多字段的结构体而需要手动编写大量的设置选项函数的代码? 本文介绍了 go-opioner 开源工具的安装和使用,它能够根据结构体的定义,自动生成函数选项模式的代码。
90 0
YI
|
9月前
|
缓存 IDE 算法
Go学习笔记01|Go项目的创建与运行
Go学习笔记01|Go项目的创建与运行
YI
370 0
|
10月前
|
存储 安全 算法
Go源代码解析-sema.go文件
Go源代码解析-sema.go文件
52 0
|
缓存 Go
一文掌握 Go 文件的写入操作
本文先是对 File.Write、File.WriteString、File.WriteAt 进行介绍,通过例子演示它们的使用方式;然后介绍 File.Seek,说明了它的用法;最后引出 bufio.NewWriter、Writer.WriteString、Writer.Flush,使用它们代替 File 结构体里的写入方法,可以不用频繁操作磁盘,提高写入效率。
207 1
一文掌握 Go 文件的写入操作
|
Go
4.5 GO方式安装
4.5 GO方式安装
114 0
|
存储 缓存 负载均衡
Go 网络库 Gnet 解析
Go 网络库 Gnet 解析
564 0
Go 网络库 Gnet 解析
|
SQL 前端开发 程序员
关于如何构建 Go 代码的思考
良好的应用结构可以改善开发者的体验。它可以帮助你隔离他们正在工作的内容,而不需要把整个代码库记在脑子里。一个结构良好的应用程序可以通过解耦组件并使其更容易编写有用的测试来帮助防止错误。