【问题标题】:How to initialize struct pointer via reflection如何通过反射初始化结构指针
【发布时间】:2017-07-13 00:33:14
【问题描述】:

我的目标是获取一个指向结构(但可以是任何类型)的nil 指针,作为interface{} 传递,并在其位置初始化一个结构。

我的测试代码(playground link)是:

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 unmarshaler 的源代码和其他 SO 帖子,但仍然明显忽略了一些东西。

如果我剥掉外面的interface{}

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

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

【问题讨论】:

  • 使用循环意味着可能有任意多个层,但只有三层:变量的接口类型、指向值的指针和值本身。
  • @Adrian:在生产中,可能有任意数量的层。但在这里,显然没有。我已经删除了循环。
  • 如何得到任意数量的层?
  • @Adrian: SomeUnmarshalWrapper(&result) 后跟 func SomeUnmarshalWrapper(i interface{}) { json.Unmarshal(&i) ... 或类似的。

标签: go reflection


【解决方案1】:

试试这个:

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

您也可以分配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 example

【讨论】:

  • 我支持这个答案。学到了一些新东西:)
  • 有没有办法设置xy?因为x 仍然是(*Foo)(nil)
  • 已经找到了。您需要将指针传递给指针。我问了我的问题here,得到了回答。以防万一有人想知道。
【解决方案2】:

在不了解您的实际用例的情况下,很难说出您需要什么解决方案,但是以 this works 为例:

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

【讨论】:

    【解决方案3】:

    在您的示例中,rv.CanAddr()false,因此您无法设置它(rv.CanSet()false)。

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

    您可以让您的示例以不同的方式工作:

    • 将 x 初始化为非零(可能不是这里的重点)
    • 使用 reflect.New(..) 将 y 设置为新值

      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)
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多