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


总结

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

相关文章
|
6月前
|
算法 测试技术 Go
【Go 编程实践】从零到一:创建、测试并发布自己的 Go 库
解释了为何需要开发自己的 Go 库,以及如何创建、测试和发布。文章以 Asiatz 库为例,详细阐述了创建目录、初始化项目、编写代码、测试、编写文档和发布等步骤,并强调了开发自己的 Go 库的优点,包括代码复用性、可维护性和可测试性。
236 0
【Go 编程实践】从零到一:创建、测试并发布自己的 Go 库
|
7月前
|
Java 编译器 Go
Go 语言 入门 && 基于 GoLand 2023.1 创建第一个Go程序
Go 语言 入门 && 基于 GoLand 2023.1 创建第一个Go程序
60 0
|
5月前
|
算法 Go PHP
GO 比较两个对象是否相同
GO 比较两个对象是否相同
|
7月前
|
Go 开发工具 git
Go 1.18 新增三大功能之一“工作区模式”介绍
Go 1.18 新增三大功能之一“工作区模式”介绍
46 0
|
10月前
|
Go 开发者
[Go开源工具] go-optioner:轻松生成函数选项模式代码
你是否使用过 functional options 函数选项模式?在使用时,你是否遇到过多字段的结构体而需要手动编写大量的设置选项函数的代码? 本文介绍了 go-opioner 开源工具的安装和使用,它能够根据结构体的定义,自动生成函数选项模式的代码。
91 0
YI
|
10月前
|
缓存 IDE 算法
Go学习笔记01|Go项目的创建与运行
Go学习笔记01|Go项目的创建与运行
YI
372 0
|
11月前
|
存储 安全 算法
Go源代码解析-sema.go文件
Go源代码解析-sema.go文件
52 0
|
11月前
|
存储 算法 Java
Go源代码解析-slice.go文件
Go源代码解析-slice.go文件
63 0
|
缓存 Go
一文掌握 Go 文件的写入操作
本文先是对 File.Write、File.WriteString、File.WriteAt 进行介绍,通过例子演示它们的使用方式;然后介绍 File.Seek,说明了它的用法;最后引出 bufio.NewWriter、Writer.WriteString、Writer.Flush,使用它们代替 File 结构体里的写入方法,可以不用频繁操作磁盘,提高写入效率。
208 1
一文掌握 Go 文件的写入操作
|
Go
4.5 GO方式安装
4.5 GO方式安装
115 0