Golang反思。价值行为

时间:2013-06-30 00:37:23

标签: reflection go

我目前对golangs反射包的行为感到绝望,这对我来说似乎并不一致。

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

如果我用任何结构作为参数调用它,它会打印“This 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()

因为这感觉不对。

4 个答案:

答案 0 :(得分: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(某个结构)或接口{}中的文件。它假设您传递了一个os.File并向您显示Kind“struct”。

但是,当您将指针传递给接口{}时,您将传递一个可以修改的接口{}变量。您没有通过基础具体类型,这会产生重要影响。您可以.Set()任何东西,而不仅仅是原始混凝土类型允许的内容。您也无法编辑单个字段,因为接口{}中的任何内容都不可分配。如果具体类型实际上是指针,则可以进行第四次取消引用(.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

答案 1 :(得分:1)

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

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。

答案 2 :(得分:0)

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

答案 3 :(得分:0)