如何通过反射初始化结构体指针

huangapple go评论112阅读模式
英文:

How to initialize struct pointer via reflection

问题

我的目标是将一个nil指针转换为一个结构体(但可以是任何类型),并将其作为interface{}传递,并在原地初始化一个结构体。

我的测试代码(playground链接)如下:

package main

import (
	"fmt"
	"reflect"
)

type Foo struct {
	Foo string
}

func main() {
	var x *Foo
	var y interface{} = x
	fmt.Printf("Before: %#v\n", y)
	fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))

	rv := reflect.ValueOf(y)
	rv.Set(reflect.New(rv.Type().Elem()))

	fmt.Printf("After: %#v\n", y)
}

希望代码本身能够说明问题。但目标基本上是将y从一个未初始化的指向Foo的指针((*main.Foo)(nil))转换为指向已初始化(零值)的Foo实例的指针:&main.Foo{Foo:""}。但是我得到了reflect.Value.Set using unaddressable value的错误。我不明白为什么我尝试设置的值是不可寻址的。我已经花了一整天的时间阅读标准库JSON解码器的源代码和其他SO帖子,但显然还是忽略了一些东西。

如果我去掉外层的interface{}

rv := reflect.ValueOf(y).Elem() // 去掉外层的interface{}
rv.Set(reflect.New(rv.Type().Elem()))

错误变成了reflect: call of reflect.Value.Type on zero Value

英文:

My goal is to take a nil pointer to a struct (but could be any type), passed as an interface{}, and initialize a struct in its place.

My test code (playground link) is:

package main

import (
	"fmt"
	"reflect"
)

type Foo struct {
	Foo string
}

func main() {
	var x *Foo
	var y interface{} = x
	fmt.Printf("Before: %#v\n", y)
	fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))

	rv := reflect.ValueOf(y)
	rv.Set(reflect.New(rv.Type().Elem()))

	fmt.Printf("After: %#v\n", y)
}

I hope the code is self-documenting. But the goal is essentially to convert y which begins as an uninitialized pointer to Foo, ((*main.Foo)(nil)) into a pointer to an initialized (zero-value) instance ofFoo: &main.Foo{Foo:""}. But I'm getting reflect.Value.Set using unaddressable value. I don't understand why the value I'm attempting to set is unaddressable. I've spent the day reading through the source code to the standard library JSON unmarshaler, and other SO posts, but am still clearly overlooking something.

If I peel away the outer interface{}:

rv := reflect.ValueOf(y).Elem() // Remove the outer interface{}
rv.Set(reflect.New(rv.Type().Elem()))

the error becomes reflect: call of reflect.Value.Type on zero Value.

答案1

得分: 4

请尝试这样做:

var x *Foo
var y interface{} = x
fmt.Printf("Before: %#v\n", y)
fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))

// 必须取得 y 的地址来设置它。使用 Elem() 解引用以获取 y 的值
rv := reflect.ValueOf(&y).Elem()

// 接口元素类型是 *main.Foo,使用 Elem() 解引用以获取 main.Foo
t := rv.Elem().Type().Elem()
rv.Set(reflect.New(t))
fmt.Printf("After: %#v\n", y)

playground 示例

你也可以通过赋值给 y 而不是使用反射来设置它:

var x *Foo
var y interface{} = x
fmt.Printf("Before: %#v\n", y)
fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))
rv := reflect.ValueOf(y)
t := rv.Type().Elem()
y = reflect.New(t).Interface()
fmt.Printf("After: %#v\n", y)

playground 示例

英文:

Try this:

var x *Foo
var y interface{} = x
fmt.Printf("Before: %#v\n", y)
fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))

// Must take address of y to set it. Dereference with Elem() to get value for y
rv := reflect.ValueOf(&y).Elem()

// Interface element type is *main.Foo, dereference with Elem() to get main.Foo
t := rv.Elem().Type().Elem()
rv.Set(reflect.New(t))
fmt.Printf("After: %#v\n", y)

playground example

You can also assign y instead of setting it via reflection:

var x *Foo
var y interface{} = x
fmt.Printf("Before: %#v\n", y)
fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))
rv := reflect.ValueOf(y)
t := rv.Type().Elem()
y = reflect.New(t).Interface()
fmt.Printf("After: %#v\n", y)

playground example

答案2

得分: 1

在你的示例中,rv.CanAddr()的返回值为false,因此你无法设置它(rv.CanSet()的返回值也是false)。

问题在于,如果你有一个指向nil的指针,它是不可寻址的。然而,指向nil的指针的指针(或指向接口的指针)是可寻址的。

你可以通过不同的方式使你的示例工作起来:

  • x初始化为非nil值(可能不是重点)
  • 使用reflect.New(..)y设置为新的Value
func main() {
    var x *Foo
    var y interface{} = x
    fmt.Printf("Before: %#v\n", y)
    fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))

    rv := reflect.ValueOf(y)
    y = reflect.New(rv.Type().Elem()).Interface()

    fmt.Printf("After: %#v\n", y)
}
英文:

In your example rv.CanAddr() is false and consequently you cannot set it (rv.CanSet() is false).

Problem: if you have a pointer to nil it is not addressable. A pointer to a pointer to nil (or a pointer to an interface) however is addressable.

You can get your example to work in different ways:

  • initialize x to be non-nil (probably not the point here)

  • set y to a new Value with reflect.New(..)

     func main() {
     	var x *Foo
     	var y interface{} = x
     	fmt.Printf("Before: %#v\n", y)
     	fmt.Printf("Goal: %#v\n", interface{}(&Foo{}))
    
     	rv := reflect.ValueOf(y)
     	y = reflect.New(rv.Type().Elem()).Interface()
    
     	fmt.Printf("After: %#v\n", y)
     }
    

答案3

得分: 1

根据你的示例,不知道你的实际用例,很难确定你需要什么解决方案,但是这个链接提供了一个解决方案:

var x *Foo
var y interface{} = x
rv := reflect.ValueOf(y)
y = reflect.New(rv.Type().Elem()).Interface()
英文:

Without knowing your actual use case, it's hard to say what solution you require, however this works for your example:

var x *Foo
var y interface{} = x
rv := reflect.ValueOf(y)
y = reflect.New(rv.Type().Elem()).Interface()

huangapple
  • 本文由 发表于 2017年7月12日 23:12:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/45061568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定