反映访问的地图不提供可修改的值

时间:2018-03-07 08:56:27

标签: dictionary go reflection slice

上下文

在编写通用差异和补丁算法的过程中,我遇到了反射问题。

当我尝试在切片中进行修补时,我没有问题,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

如何通过反射从地图中获取可修改的值?

感谢您的时间。

1 个答案:

答案 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"]获得的可能指针可能不存在(或者可能指向另一个值)使用它。为了避免这种混乱和运行时间恐慌,首先不允许这样做。