如何在 Go 中复制接口值? [英] How to copy an interface value in Go?

查看:14
本文介绍了如何在 Go 中复制接口值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在 Go 中复制接口值?

How to copy an interface value in Go?

我的用户界面:

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

我的Admin结构:

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
", user1.Name())

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

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

如何实现改副本名称原不改?

How to achieve that changing the copy's name the original doesn't change?

推荐答案

这里的问题是您的 user1 变量(类型为 User)包含一个 pointerAdmin 结构.

The problem here is that your user1 variable (which is of type User) holds a pointer to an Admin struct.

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

When you assign user1 to another variable (of type User), the interface value which is a pair of the dynamic type and value (value;type) will be copied - so the pointer will be copied which will point to the same Admin struct. So you only have one Admin struct value, both user1 and user2 refer (point) to this. Changing it through any of the interface values changes the one and only value.

要使 user1user2 不同,您需要 2 个底层"Admin 结构.

To make user1 and user2 distinct, you need 2 "underlying" Admin structs.

一种方法是type assert user1 中的值接口值,并复制该结构体,并将其地址包装在另一个 User 值中:

One way is to type assert the value in the user1 interface value, and make a copy of that struct, and wrap its address in another User value:

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 上试试):>

Now they will be distinct, output (try it on the Go Playground):

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

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

Of course this solution has its limitation: the dynamic type stored in the User interface value is "wired" in the solution (*Admin).

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

If we want a "general" solution (not just one that works with *Admin), we can use reflection (reflect package).

为简单起见,我们假设 user1 始终包含一个指针(目前).

For simplicity let's assume user1 always contains a pointer (for now).

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

Using reflection we can get the dynamic type (here *Admin), and even the dynamic type without a pointer (Admin). And we can use reflect.New() to obtain a pointer to a new value of that type (whose type will be identical to the original dynamic type in user1 - *Admin), and wrap this back into a User. This is how it could look like:

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

输出(在 Go Playground 上试试这个):

Output (try this one on the Go Playground):

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

请注意,reflect.New() 将创建一个新值,该值被初始化为零值(因此它不会是原始值的副本).这不是问题,因为 Admin 只有一个字段,无论如何我们都将更改该字段,但通常必须牢记在心.

Note that reflect.New() will create a new value which is initialized to its zero value (so it will not be a copy of the original). It's not a problem here as Admin only has one field which we're about to change anyway, but must be kept on our mind in general.

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

Our initial assumption was that user1 contains a pointer. Now the "full" solution must not make such assumption. If the value in user1 would not be a pointer, this is how it could be "cloned":

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 中复制接口值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆