使用反射更改界面下的指针类型和值 [英] Changing pointer type and value under interface with reflection

查看:176
本文介绍了使用反射更改界面下的指针类型和值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我可以用反射来改变指针的值: v.Elem ().Set(reflect.ValueOf(& Greeter {Jack}).elem()),它相当于 a =& Greeter {Jack}



但是我怎样才能做一个相当于 a =& Greeter2 {Jack}



UPDATE:不幸的是,在我试图解决的实际问题中,我无法解决原始变量( panic:reflect:reflect.Value.Set using unaddressable值),这就是为什么我试图在指针级别解决。



  package main 

导入(
fmt
反映


类型格林特结构{
名称字符串
}
$ b $ func(g * Greeter)String()string {
returnHello,My name is+ g.Name
}

type Greeter2 struct {
名称字符串
}

func(g * Greeter2)String()string {
returnHello,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:main.Greeter2类型的值不能分配给main.Greeter
fmt.Println(a.String())//你好,我的名字是Jack

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


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



您有一个变量 a 的接口类型。如果你想修改这个变量的值,你必须传递/使用这个变量的地址。

.String()方法知道哪个被调用:

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

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

  var a fmt.Stringer 
a =& Greeter {John }
fmt.Println(a.String())//你好,我的名字是John

v:= reflect.ValueOf(& a).Elem()
v.Set(reflect.ValueOf(& Greeter2 {Jack}))
fmt.Println(a.String())// Hello2,我的名字是Jack

a =& amp ; Greeter2 {Ron}
fmt.Println(a.String())// Hello2,我的名字是Ron

输出(在 Go Playground ):

 您好,我的名字是John 
Hello2,我的名字是Jack
Hello2,我的名字是Ron

没有改变接口的值和类型的东西,只能改变一个变量(有内存地址)中的数据,这个变量可能是接口类型的。



所以关键是:
$ b


  1. 从地址开始: reflect.ValueOf( & a)

  2. 您必须设置一个指针值为 * Greeter2 implements fmt.String ,但不是 Greeter 。请参阅转到,X不会实现Y(...方法有一个指针接收器)

有关更多详细信息和深入介绍,阅读:围棋博客:反思的法律



编辑:关于修改副本

我的答案的第一部分:无论何时您传递某个副本。这还包括将接口值传递给 reflect.ValueOf():它也会收到一份副本,所以你不能避免这种情况。唯一的方法是传递一个指针;因为指针也会被复制,但我们正在修改指向的值(而不是指针),它是相同的。


Is it possible to change pointer type and value of variable defined by interface?

I can change pointer value with reflection: v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem()) which is equivalent to a = &Greeter{"Jack"}.

But how can I make a reflection equivalent for a = &Greeter2{"Jack"}?

UPDATE: Unfortunately in real problem that I'm trying to solve I can not address original variable (panic: reflect: reflect.Value.Set using unaddressable value), that's why I'm trying to workaround at pointer level.

Here is full example code:

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
}

解决方案

Everything in Go is passed by value. Interfaces too. When you pass a value of interface type, a copy of the interface value will be made (along with the (value;type) inside it), and you will only be able to modify the copy which will not affect the original.

You have a variable a of interface type. If you want to modify the value stored in this variable, you have to pass / use the address of this variable.

First let's modify the Greeter2.String() method to know which one gets called:

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

And also call a.String() after first init to see that it originally does contain a *Greeter value as we'll soon change it.

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

Output (try it on the Go Playground):

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

So essentially there is no such thing as changing the value and type of an interface value, only changing the data in a variable (that has a memory address) that may be of interface type.

So the key things were:

  1. Start from an address: reflect.ValueOf(&a)
  2. And you have to set a pointer value into it as only *Greeter2 implements fmt.String, but not Greeter. See Go, X does not implement Y (... method has a pointer receiver) for details.

For more details and in-depth introducation, read: The Go Blog: The Laws of Reflection

Edit: about modifying a copy

First section of my answer: Whenever you pass something a copy is made. This also includes passing an interface value to reflect.ValueOf(): that will also receive a copy, so you can't avoid this. Only way to make this work is to pass a pointer; because the pointer will also be copied, but we're modifying the pointed value (not the pointer) which is the same.

这篇关于使用反射更改界面下的指针类型和值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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