这里的问题是,您的user1
变量(类型为User
)包含pointer到Admin
的 struct .
当您将user1
赋给另一个变量(类型为User
)时,将复制作为动态类型和值(value;type)
对的接口值-因此将复制指向相同Admin
struct 的指针.所以您只有一个Admin
struct 值,user1
和user2
都引用(指向)这一点.通过任何接口值更改它会更改唯一的值.
要区分user1
和user2
,需要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")