使用反射

时间:2017-09-21 11:01:06

标签: pointers go reflection interface

是否可以更改接口定义的变量的指针类型和值?

我可以使用反射更改指针值:v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem()),相当于a = &Greeter{"Jack"}

但是如何为a = &Greeter2{"Jack"}制作等效的反射?

更新:不幸的是,在我试图解决的实际问题中,我无法解决原始变量(panic: reflect: reflect.Value.Set using unaddressable value),这就是我尝试在指针级别进行解决的原因。

Here是完整的示例代码:

package main

import (
    "fmt"
    "reflect"
)

type Greeter struct {
    Name string
}

func (g *Greeter) String() string {
    return "Hello, My name is " + g.Name
}

type Greeter2 struct {
    Name string
}

func (g *Greeter2) String() string {
    return "Hello, My name is " + g.Name
}

func main() {
    var a fmt.Stringer
    a = &Greeter{"John"}

    v := reflect.ValueOf(a)
    v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())
    //v.Elem().Set(reflect.ValueOf(&Greeter2{"Jack"}).Elem()) // panic: reflect.Set: value of type main.Greeter2 is not assignable to type main.Greeter
    fmt.Println(a.String()) // Hello, My name is Jack

    a = &Greeter2{"Ron"}
    fmt.Println(a.String()) // Hello, My name is Ron
}

1 个答案:

答案 0 :(得分:3)

Go中的所有内容都按值传递。接口也是如此。当您传递接口类型的值时,将创建接口值的副本(以及其中的(value;type)),并且您将只能修改不会影响原始的副本。

您有一个接口类型的变量a。如果要修改存储在此变量中的值,则必须传递/使用此变量的地址。

首先让我们修改Greeter2.String()方法以了解哪一个被调用:

func (g *Greeter2) String() string {
    return "Hello2, My name is " + g.Name
}

并且在第一次初始化之后调用a.String()以查看它最初确实包含*Greeter值,因为我们很快就会更改它。

var a fmt.Stringer
a = &Greeter{"John"}
fmt.Println(a.String()) // Hello, My name is John

v := reflect.ValueOf(&a).Elem()
v.Set(reflect.ValueOf(&Greeter2{"Jack"}))
fmt.Println(a.String()) // Hello2, My name is Jack

a = &Greeter2{"Ron"}
fmt.Println(a.String()) // Hello2, My name is Ron

输出(在Go Playground上尝试):

Hello, My name is John
Hello2, My name is Jack
Hello2, My name is Ron

所以基本上没有改变接口的值和类型,只改变可能是接口类型的变量(具有内存地址)中的数据。 / p>

所以关键的事情是:

  1. 从地址开始:reflect.ValueOf(&a)
  2. 您必须在其中设置指针值,因为只有*Greeter2实现fmt.String,而不是Greeter。有关详细信息,请参阅Go, X does not implement Y (... method has a pointer receiver)
  3. 有关详细信息和深入介绍,请阅读:The Go Blog: The Laws of Reflection

    编辑:关于修改副本

    我的回答的第一部分:每当你传递一些副本时。这还包括将接口值传递给reflect.ValueOf():也会收到副本,因此您无法避免这种情况。使这项工作的唯一方法是传递指针;因为指针也会被复制,但是我们正在修改指向的值(而不是指针)。