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


总结

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

相关文章
|
3月前
|
SQL 关系型数据库 MySQL
go如何使用SQLX操作MySQL数据库?
sqlx是Go语言中一款流行的第三方数据库操作包,它扩展了Go标准库`database/sql`的功能,极大地简化了数据库操作流程并提供了丰富的数据库交互方法。
|
3月前
|
搜索推荐 Go UED
揭秘 Go 中的模板:一份全面而广泛的指南
揭秘 Go 中的模板:一份全面而广泛的指南
|
设计模式 算法 Go
Go的模板方式模式
模板方法模式是一种行为设计模式,它在一个抽象类中定义了一个算法的骨架,允许子类为算法的某些步骤提供具体实现。它促进了代码的重用,并为一组相关算法定义了一个通用的结构。
56 0
|
Go
Go 删除文件方式
Go 删除文件方式
125 0
|
Go 开发者
[Go开源工具] go-optioner:轻松生成函数选项模式代码
你是否使用过 functional options 函数选项模式?在使用时,你是否遇到过多字段的结构体而需要手动编写大量的设置选项函数的代码? 本文介绍了 go-opioner 开源工具的安装和使用,它能够根据结构体的定义,自动生成函数选项模式的代码。
139 0
|
安全 测试技术 Go
Go1.18 正式发布,揭秘三大特性
Go1.18 正式发布,揭秘三大特性
YI
|
缓存 IDE 算法
Go学习笔记01|Go项目的创建与运行
Go学习笔记01|Go项目的创建与运行
YI
484 0
|
存储 安全 算法
Go源代码解析-sema.go文件
Go源代码解析-sema.go文件
75 0
|
缓存 Go
一文掌握 Go 文件的写入操作
本文先是对 File.Write、File.WriteString、File.WriteAt 进行介绍,通过例子演示它们的使用方式;然后介绍 File.Seek,说明了它的用法;最后引出 bufio.NewWriter、Writer.WriteString、Writer.Flush,使用它们代替 File 结构体里的写入方法,可以不用频繁操作磁盘,提高写入效率。
310 1
一文掌握 Go 文件的写入操作
|
存储 缓存 算法
Go --- GCache缓存官方例子
Go --- GCache缓存官方例子