我目前对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()
因为这感觉不对。
答案 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)