0

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

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

因为这在某种程度上感觉不对。

4

4 回答 4

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

于 2013-06-30T06:47:37.217 回答
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。

于 2013-06-30T01:09:18.637 回答
0

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

于 2013-06-30T01:14:50.427 回答
0

即使不使用反射,存储在映射中的值也是不可寻址的。

http://play.golang.org/p/wYLeJ3W4R2

http://play.golang.org/p/ttUGBVh1lc

https://groups.google.com/forum/#!topic/golang-nuts/jzjEXoc9FwU

https://groups.google.com/forum/#!topic/golang-nuts/V_5kwzwKJAY

于 2013-11-28T12:57:07.853 回答