【问题标题】:instantiate by reflection通过反射实例化
【发布时间】:2021-03-23 03:43:22
【问题描述】:

给定一个带参数的函数(即指向结构的指针),我想实例化此参数的一个类型。

例如,对于这个函数:

func MyFunction(myStruct *MyStruct) {}

使用反射我想创建一个包含与x := &MyStruct{} 完全相同的变量。

这是代码示例:

package main

import (
    "fmt"
    "reflect"
)

type MyStruct struct {
}

func main () {
    reflectedFunction := reflect.TypeOf(MyFunction)
    argType := reflectedFunction.In(0)

    reflectedParameter := reflect.New(argType)
    actual := reflectedParameter.Interface()
    fmt.Println(actual)

    expected := &MyStruct{}
    fmt.Println(expected)
}

func MyFunction(myStruct *MyStruct) {
}

如果你执行它,你会看到它们包含不同的信息:

0xc00000e028 // actual
&{} // expected

这个问题不是关于我为什么要这样做,所以请避免建议不要这样做等。

【问题讨论】:

  • 不一样
  • 我设法使用 Elem().Interface() 获得了“相同”的值,打印返回的结果并不完全相同,但似乎内容实际上是相同的(使用 printf 检查%#v)
  • 阅读所有评论并回答。这一直有争议......
  • @fj123x。 Elem().Interface() 为您提供相同的 type,但它仍然是一个 nil 指针。您必须首先使用具有正确类型的reflect.New。您无法动态获取*MyStruct 的实际变量,因为类型是在编译时定义的,因此您必须满足于包含该类型的接口。

标签: go reflection


【解决方案1】:

在您的代码中,actual 是一个包含*MyStruct 的interface{} 值。正如名称和文档所示,reflectedParameter.Interface() 返回一个 interface{}。

使用反射我想创建一个包含与x := &MyStruct{} 完全相同的变量。

然后你必须输入 assert 它:

actual := reflectedParameter.Elem().Interface().(*MyStruct)

【讨论】:

  • 不,你需要 .Elem().Interface() play.golang.org/p/r5i3tvlD8He
  • @mh-cbon,不,你需要reflect.New(argType.Elem())
  • @mh-cbon 仍然会产生 interface{} 类型的值,而不是问题所述的 &MyStruct 类型的值。
  • 更新答案。
  • @fj123x 如果你不能做类型断言,那么你就不能满足你的要求。变量类型是在编译时定义的,只有值类型可以在运行时改变。
【解决方案2】:

reflect.New 创建一个指向反射类型的新零值的指针。在您的示例中,该类型已经是*MyStruct,因此您的actual 的值最终成为**MyStruct 的表示,如https://play.golang.org/p/Nyuc0mYmgkZ 所示。使用 .Elem() 会再次得到正确的类型,但最终会得到 nil 指针 (*MyStruct)(nil)。

如果你想创建一个新的指针值,你需要使用.Elem()如果是第一种类型。

reflectedParameter := reflect.New(argType.Elem())

https://play.golang.org/p/QzwTFUH3HTs

reflectedFunction := reflect.TypeOf(MyFunction)
argType := reflectedFunction.In(0)

reflectedParameter := reflect.New(argType.Elem())
actual := reflectedParameter.Interface()
fmt.Printf("%#v\n", actual)

expected := &MyStruct{}
fmt.Printf("%#v\n", expected)

打印出来的

&main.MyStruct{}
&main.MyStruct{}

【讨论】:

  • 这不适合“我想创建一个包含与 x := &MyStruct{} 将包含的完全相同的变量。”这会产生一个interface{},OP 所描述的会产生一个*MyStruct。
  • @Adrian 我真的不同意这个论点,因为沿着这条路(反射)你会坚持界面
  • @Adrian:这确实会创建一个 *MyStruct 类型的值,您可以在示例中看到它。将 interface{} 传递给 fmt.Printf interface{} 参数,或者先声明它没有区别。
  • 该值是一个包含*MyStruct的接口值。它与x := &MyStruct{}完全相同。和var x interface{} = &MyStruct一模一样。
  • @Adrian,很明显你不能在没有类型断言的情况下得到一个类型化的值,但问题中的主要错误是因为用户创建了一个**MyStruct
猜你喜欢
  • 2020-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多