如何在Go中复制接口值?

我的User界面:

type User interface {
    Name() string
    SetName(name string)
}

我的Admin struct :

type Admin struct {
    name string
}

func (a *Admin) Name() string {
    return a.name
}

func (a *Admin) SetName(name string) {
    a.name = name
}

我试着复制user1的值.

主要功能:

func main() {
    var user1 User
    user1 = &Admin{name:"user1"}

    fmt.Printf("User1's name: %s\n", user1.Name())

    var user2 User
    user2 = user1
    user2.SetName("user2")

    fmt.Printf("User2's name: %s\n", user2.Name()) // The name will be changed as "user2"
    fmt.Printf("User1's name: %s\n", user1.Name())  // The name will be changed as "user2" too, How to make the user1 name does not change?
}

如何实现原件改名不变?

推荐答案

这里的问题是,您的user1变量(类型为User)包含pointerAdmin的 struct .

当您将user1赋给另一个变量(类型为User)时,将复制作为动态类型和值(value;type)对的接口值-因此将复制指向相同Admin struct 的指针.所以您只有一个Admin struct 值,user1user2都引用(指向)这一点.通过任何接口值更改它会更改唯一的值.

要区分user1user2,需要2个"底层"Admin struct .

一种方法是将user1接口值中的值设置为type assert,并复制该 struct ,然后将其地址包装为另一个User值:

var user2 User
padmin := user1.(*Admin) // Obtain *Admin pointer
admin2 := *padmin        // Make a copy of the Admin struct
user2 = &admin2          // Wrap its address in another User
user2.SetName("user2")

现在它们将是截然不同的输出(在Go Playground上试试):

User1's name: user1
User2's name: user2
User1's name: user1

当然,此解决方案有其局限性:存储在User接口值中的动态类型在解决方案(*Admin)中是"连接的".

使用反射

如果我们想要一个"通用"的解决方案(不仅仅是使用*Admin的解决方案),我们可以使用反射(reflect个包).

为简单起见,我们假设user1总是包含一个指针(目前是这样).

使用反射,我们可以获得动态类型(这里是*Admin),甚至可以获得没有指针的动态类型(Admin).我们可以使用reflect.New()来获取指向该类型(其类型将与user1-*Admin中的原始动态类型相同)的新值的指针,并将其包装回User中.它看起来可能是这样的:

var user3 User
user3 = reflect.New(reflect.ValueOf(user1).Elem().Type()).Interface().(User)
user3.SetName("user3")

输出(在Go Playground上try 此输出):

User1's name: user1
User3's name: user3
User1's name: user1

请注意,reflect.New()将创建一个初始化为零值的新值(因此它不会是原始值的副本).这不是一个问题,因为Admin只有一个领域,我们无论如何都要改变它,但总的来说,我们必须记住它.

我们最初的假设是user1包含一个指针.现在,"全面"解决方案不能做出这样的假设.如果user1中的值不是指针,那么它可以这样"克隆":

var user3 User
if reflect.TypeOf(user1).Kind() == reflect.Ptr {
    // Pointer:
    user3 = reflect.New(reflect.ValueOf(user1).Elem().Type()).Interface().(User)
} else {
    // Not pointer:
    user3 = reflect.New(reflect.TypeOf(user1)).Elem().Interface().(User)
}
user3.SetName("user3")

Go相关问答推荐

GetSecretValue,get identity:get credentials:无法刷新缓存的凭据

如何使用Promela建模语言对Golang RWLock进行建模

为什么我不能使用Docker从本地访问我的Gin应用程序?

有没有办法通过Go cmdline或IDE(IntelliJ)找出我的 struct 实现了什么接口?

如何使用中间件更改http请求的响应代码?

go测试10m后如何避免超时

Kafka golang 生产者在错误后更改分区计数

使用 goroutine 比较 Golang 中的两棵树是等价的

Go 中如何判断 struct 体是否包含另一个 struct 体?

当我使用 CircleCI 构建 Go Image 时,我得到runtime/cgo: pthread_create failed: Operation not allowed

如何将Golang测试用例的测试覆盖率值与特定阈值进行比较

在 Go 中解组编号的 XML 标签

如果值为 false,gRPC 不返回布尔值

如何匹配两次出现的相同但随机字符串之间的字符

在 Golang 中,如何将接口作为泛型类型与 nil 进行比较?

如何使用struct的方法清除除某些字段之外的struct值

如何在没有内存分配的情况下压缩和发布文件

什么是无效字符实体 &ccb

golangci-lint 的 GitHub 操作失败,无法加载 fmt

如何动态解析 Go Fiber 中的请求正文?