【问题标题】:Golang reflection.Value behaviourGolang 反射.值行为
【发布时间】:2013-06-27 11:36:17
【问题描述】:

我目前对 golangs reflect 包的行为感到绝望,对我来说这似乎根本不一致。

1) 据我了解,reflect.Value 似乎带有一个指向基础值的指针。 例如。如果我打电话

var s string
v1 := reflect.ValueOf(&s).Elem()
v2 := v1
v2.SetString("Hello World!")
fmt.Println(s)

它打印我“Hello World!”。 但是,这似乎不适用于通过调用 Field() 获得的 reflect.Value。

val := ... //Assign a reflect.Value to it
nextval := val.Field(0) //Make sure that Field exists and is of type map
nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
fmt.Println(nextval.MapKeys()
fmt.Println(val.Field(index).MapKeys())

打印出来

[Some_value_of_type_KEY]
[]

这是一个主要的烦恼。有人知道为什么会这样吗?

================================================ ====

2) 考虑函数

func Test(v interface{}) {
    val := reflect.ValueOf(v)
    if val.Kind() != reflect.Struct {
        fmt.Println("It is a struct")
    }
}

如果我用任何结构作为参数调用它,它会打印“这是一个结构”。 但是,我将无法使用 val 为 v 中的内容分配新值, 由于该值不可寻址。通过以下方式解决:

func Test(v interface{}) {
    val := reflect.ValueOf(&v).Elem()
    if val.Kind() != reflect.Struct {
        fmt.Println("This never get's printed!")
    }
}

根据文档,我假设通过使用 '&' 我使用指向 v 的指针并通过调用 Elem() 我得到它指向的元素,因此 val.Kind() 仍然应该返回同一件事情。它没有。 val.Kind() 现在是一个 reflect.Interface。

有没有办法不用去

valForTestingKind := reflect.ValueOf(v)
valForSettingNewValue := reflect.ValueOf(&v).Elem()

因为这感觉有点不对。

【问题讨论】:

    标签: reflection go


    【解决方案1】:
    【解决方案2】:

    第 1 部分:

    通过分配给nextval,您破坏了它与原始val 的关联。相反,请使用Set() 方法。

    nextval.Set(reflect.MakeMap(reflect.MapOf(KEY, ELEM)))
    

    Set() 相当于反射世界中的赋值。当然,您必须确保它可以使用 reflect.ValueOf(&v).Elem() 分配,就像您在第一个代码示例中所做的那样。


    第 2 部分:

    这里的问题是你有另一个级别的间接性。 v 是 interface{} 类型,并且具有类型为 Kind 结构的具体值。就像每个接受接口类型参数的函数一样,当您调用reflect.ValueOf 时,参数会自动转换为该类型。但是,将一个接口转换为另一个接口会导致具体值在新的接口类型中重新装箱。重新装箱之前的类型信息会丢失。例如,接受 io.Writer 的函数不会知道调用函数将其视为 io.ReaderWriter。

    在这种情况下,这意味着 reflect.ValueOf 无法判断您传递的是 os.File(某些结构)还是装在 interface{} 中的文件。它假定您传递了一个 os.File 并向您显示 Kind “结构”。

    但是,当您传递一个指向 interface{} 的指针时,您传递的是一个可以修改的 interface{} 变量。你没有传递底层的具体类型,这会产生重要的后果。您可以.Set() 任何东西,而不仅仅是原始具体类型所允许的。您也不能编辑单个字段,因为 interface{} 中的任何内容都是不可分配的。如果具体类型实际上是一个指针,您可以执行第四次取消引用 (.Elem()) 并从那里修改字段。

    那么,就代码而言,这意味着什么?

    //let v = an interface{} with a concrete type of SomeStruct
    val := reflect.ValueOf(&v).Elem()
    fmt.Println(val.Elem().Kind())                // struct
    val.Elem().Field(0).Set(10)                   // PANIC! Field isn't assignable.
    val.Set("a string which is not a SomeStruct")
    fmt.Println(val.Elem().Kind())                // string
    

    我这里做了一个例子:http://play.golang.org/p/6MULn3KoNh

    【讨论】:

      【解决方案3】:

      您尚未展示完整且可编译的代码。您是传递指向结构的指针还是按值传递结构?在后一种情况下,反射不能改变它。

      【讨论】:

        【解决方案4】:

        我想谈谈你的第二段代码:

        val := ... //Assign a reflect.Value to it
        nextval := val.Field(0) //Make sure that Field exists and is of type map
        nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
        nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
        fmt.Println(nextval.MapKeys()
        fmt.Println(val.Field(index).MapKeys())
        

        在第三行,您将一个新的、不同的对象重新分配给变量nextval。您不应该在 nextval 上调用某种设置方法而不是重新分配它吗?在您的第一个示例中,您调用了SetString,但在此示例中,您只是重新分配了变量,这可能就是行为不同的原因。重新分配变量后,nextval 将不再以任何方式连接到 val.Field(0)。还有,index是什么?

        如果这不能解释您的问题,请编辑问题以包含一个简短、独立、正确、可编译的示例 (SSCCE)。我希望能够将其发布到 golang.org 的首页上的文本框中,以便查看问题。如果可能,您应该始终发布 SSCCE。

        【讨论】:

        • 是的,不使用 Set() 是我的错误。
        猜你喜欢
        • 1970-01-01
        • 2017-10-11
        • 1970-01-01
        • 2017-12-11
        • 2016-06-03
        • 2021-07-05
        • 2022-01-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多