在编写通用差异和补丁算法的过程中,我遇到了反射问题。
当我尝试在切片中进行修补时,我没有问题,reflect.ValueOf(&slice).Elem().Index(0).CanSet()
返回true。这允许我修改切片元素内的任何内容,无论是原始切片还是结构切片。
然而,当我尝试使用地图reflect.ValueOf(&map).Elem().MapIndex(reflect.ValueOf("key")).CanSet()
时,返回false。这使我无法尝试对地图内容进行任何操作。
切片
s := []string{"a", "b", "c"}
v := reflect.ValueOf(&s).Elem()
e := v.Index(1)
println(e.String())
println(e.CanSet())
e.Set(reflect.ValueOf("d"))
for _, v := range s {
print(v, " ")
}
output :
b
true
a d c
地图
m := map[string]string{
"a": "1",
"b": "2",
"c": "3"}
mv := reflect.ValueOf(&m).Elem()
println(mv.MapIndex(reflect.ValueOf("a")).CanSet())
output:
false
如何通过反射从地图中获取可修改的值?
感谢您的时间。
答案 0 :(得分:4)
这不是reflect
包的限制。切片索引表达式是可寻址的(例如,&s[0]
是有效的),因此通过反射获得的切片元素将是可设置的。映射索引表达式不可寻址(例如&m["a"]
无效),因此通过反射获得的键值将无法设置。请参阅相关的How to update map values in Go
只能设置可寻址的值,尝试“设置”不可寻址的值只能修改副本(而不是原始值),因此首先不允许这样做。引自Value.CanSet()
:
CanSet报告是否可以更改v的值。如果值是可寻址的,则只能更改值,并且未使用未导出的结构字段获取值。
如果要使用反射更改分配给地图中某个键的值,请使用Value.SetMapIndex()
方法:
mv.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf("11"))
fmt.Println(m)
输出将是(在Go Playground上尝试):
map[b:2 c:3 a:11]
注意:不允许获取地图索引表达式的地址(并允许使用反射更改值),因为地图的内部可能随时更改(例如,如果将新的键值添加到地图中,实现可能必须重新构造它在内部存储键值对的方式,因此当你结束时,&m["a"]
获得的可能指针可能不存在(或者可能指向另一个值)使用它。为了避免这种混乱和运行时间恐慌,首先不允许这样做。