如何在 Go 中复制接口值?

如何在 Go 中复制接口值?


我的User界面:


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\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?

}

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


喵喵时光机
浏览 282回答 3
3回答

扬帆大鱼

这里的问题是您的user1变量(类型为User)包含一个指向结构的指针Admin。当您分配user1给另一个变量(类型User)时,将复制作为动态类型和值对的接口值(value;type)- 因此将复制指向同一Admin结构的指针。所以你只有一个Admin结构值,两者都user1引用user2(指向)这个。通过任何接口值更改它会更改唯一值。要制作user1和user2区分,您需要 2 个“基础”Admin结构。一种方法是在接口值中键入断言user1值,并制作该结构的副本,并将其地址包装在另一个User值中:var user2 Userpadmin := user1.(*Admin) // Obtain *Admin pointeradmin2 := *padmin        // Make a copy of the Admin structuser2 = &admin2          // Wrap its address in another Useruser2.SetName("user2")现在它们将是不同的,输出(在Go Playground上尝试):User1's name: user1User2's name: user2User1's name: user1当然这个解决方案有其局限性:存储在接口值中的动态类型在解决方案( )User中是“连线的” 。*Admin使用反射如果我们想要一个“通用”解决方案(不仅仅是一个适用于 的解决方案*Admin),我们可以使用反射(reflect包)。为简单起见,我们假设user1总是包含一个指针(现在)。使用反射我们可以得到动态类型(这里*Admin),甚至是没有指针的动态类型(Admin)。我们可以使用reflect.New()获取指向该类型的新值的指针(其类型将与 - 中的原始动态类型相同user1)*Admin,并将其包装回User. 这就是它的样子:var user3 Useruser3 = reflect.New(reflect.ValueOf(user1).Elem().Type()).Interface().(User)user3.SetName("user3")输出(在Go Playground上试试这个):User1's name: user1User3's name: user3User1's name: user1请注意,这reflect.New()将创建一个初始化为零值的新值(因此它不会是原始值的副本)。这不是问题,因为Admin只有一个领域我们将要改变,但总的来说必须牢记在心。我们最初的假设是user1包含一个指针。现在,“完整”解决方案绝不能做出这样的假设。如果 in 中的值user1不是指针,这就是它可以被“克隆”的方式:var user3 Userif 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")

www说

另一种解决方案可能是在您的用户界面中添加一个 Clone() 方法:type User interface {    Clone() User    Name() string    SetName(name string)}type Admin struct {    name string}func (a *Admin) Clone() User {    u := *a    return &u}func (a *Admin) Name() string {    return a.name}func (a *Admin) SetName(name string) {    a.name = name}func main() {    var user1 User    user1 = &Admin{name:"user1"}    fmt.Printf("User1's name: %s\n", user1.Name())    var user2 User    user2 = user1.Clone()    user2.SetName("user2")    fmt.Printf("User2's name: %s\n", user2.Name())    // output: User2's name: user2    fmt.Printf("User1's name: %s\n", user1.Name())    // output: User1's name: user1}当然,在这种情况下,实现会链接到接口,这可能不是我们所希望的,但这是一个解决方案。

海绵宝宝撒

有一种更通用的方法可以做到这一点。func Clone(oldObj interface{}) interface{} {&nbsp; &nbsp; newObj := reflect.New(reflect.TypeOf(oldObj).Elem())&nbsp; &nbsp; oldVal := reflect.ValueOf(oldObj).Elem()&nbsp; &nbsp; newVal := newObj.Elem()&nbsp; &nbsp; for i := 0; i < oldVal.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; newValField := newVal.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; if newValField.CanSet() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newValField.Set(oldVal.Field(i))&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return newObj.Interface()}但是,它有一个缺陷:它不能设置未导出的字段。它可以在's black magic的帮助下使用这个unsafe解决方案来解决,但我宁愿避免它。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go