我目前对golangs反射包的行为感到绝望,这对我来说似乎根本不一致。
1) 据我所知,这是一种反思。值似乎带有指向基础值的指针。例如,如果我打电话给
var s string
v1 := reflect.ValueOf(&s).Elem()
v2 := v1
v2.SetString("Hello World!")
fmt.Println(s)
上面印着"你好,世界!"。然而,这似乎并不适用于反思。通过调用Field()获得的值。
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()现在是一个反射。界面
有没有办法不去
valForTestingKind := reflect.ValueOf(v)
valForSettingNewValue := reflect.ValueOf(&v).Elem()
因为这在某种程度上感觉不对。
第1部分:
通过分配给nextval
,您就打破了它与原始val
的关联。相反,请使用Set()
方法。
nextval.Set(reflect.MakeMap(reflect.MapOf(KEY, ELEM)))
Set()
相当于反射世界中的赋值。当然,您必须确保它可以使用reflect.ValueOf(&v).Elem()
进行赋值,就像您在第一个代码示例中所做的那样。
第2部分:
这里的问题是,您有另一个间接级别。v
的类型为接口{},并且具有一个类型为Kind结构的具体值。就像每个接受接口类型参数的函数一样,当您调用reflect.ValueOf
时,参数会自动转换为该类型。但是,将一个接口转换为另一个接口会导致具体值在新的接口类型中重新混合。重新混合之前的类型信息将丢失。例如,一个接受io.Writer的函数不会知道调用函数将其视为io.ReaderWriter.
在这种情况下,它意味着反思。ValueOf无法判断您是否通过了操作系统。文件(某个结构)或装箱在接口{}中的文件。它假设你通过了os。文件并显示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
我想谈谈您的第二块代码:
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。
您还没有显示完整且可编译的代码。是传递指向结构的指针,还是按值传递结构?在后一种情况下,反射不能使其发生突变。
存储在映射中的值即使不使用反射也无法寻址。
http://play.golang.org/p/wYLeJ3W4R2
http://play.golang.org/p/ttUGBVh1lc
https://groups.google.com/forum/#!主题/golang nuts/jzjEXoc9FwU
https://groups.google.com/forum/#!topic/golang nuts/V_5kwzwKJAY