Golang Reflect 系列二 – 和 Map 操作有关的

微信扫一扫,分享到朋友圈

Golang Reflect 系列二 – 和 Map 操作有关的

通过反射方式对 map 进行操作,实际上未必会有多么复杂,它包含了如下的一系列 reflect 中的方法以及一个迭代对象 MapIter:

  • reflect.Type

    Key() Type
    Value() Type
    
  • reflect.Value

    MapKeys() []Value
    MapRange() *MapIter
    MapIndex(key Value) Value
    SetMapIndex(key, value Value)
    
  • reflect.MapIter

    Next() bool
    Key() Value
    Value() Value
    
  • reflect.MapOf(key, elem Type) Type
  • reflect.MakeMap(typ Type) Value
  • reflect.MakeMapWithSize(typ Type, n int) Value

和 Map 操作有关的

对于 Map ( reflect.Map
) 类型来说,通过反射的方式对其进行操作,并不困难。

用样例说话

获取 map 的类型

var mi = make(map[string]interface{})
typ := reflect.TypeOf(mi)
t.Log(typ)
/// Or:
var mi = make(map[string]interface{})
var mv = reflect.ValueOf(mi)
typ := mv.Type()
t.Log(typ)

通过 type 构造 map 新实例

newInstanceValue := reflect.MakeMap(typ)
t.Log(newInstanceValue.Interface())

首先你需要有一个 map 类型的 reflect.Type 表示。一般来说通过对一个已有的对象进行 reflect.TypeOf 是最简单的方法。

稍后的章节会有更复杂的示例。

MakeMap会创建一个 map 新实例 ins,并返回 &ins
的 Value 表示。由于它自动包含了 & 取地址操作,因为得到的 newInstanceValue
addressable
的,这意味着你可以直接使用 newInstanceValue
进行赋值操作(SetXXX 类操作)。

取得 map 实例的 Value 表示

// MakeMap 返回的是 map 实例的 Value 表示
newInstanceValue := reflect.MakeMap(typ)
// 从已有的实例求得其 Value 表示
var mm map[string]string
m := reflect.ValueOf(mm)

这和其它的数据类型及其实例的反射操作是完全相同的。

取得 map 实例的 keys

newInstanceValue := reflect.MakeMap(typ)
keys := newInstanceValue.MapKeys()
for i, k := range keys {
t.Logf("key %d = %v", i, k)
key := k.Convert(newInstance.Type().Key())  // 将 k 转换为 map 被定义的 key 类型
t.Logf("       = %v (%v) -> %v", key, key.Type(), newInstanceValue.MapIndex(key).String())
}

通过 val.MapKeys()
取得的是一个 []Value
,所以我们可以迭代它,此时迭代得到的 k
所代表的 map key 实际上是 interface{}
类型的,所以我们应该会需要 key := k.Convert(newInstance.Type().Key())
来对其进行拆箱、从而得到一个符合其类型定义的 map key 的 Value 表示。

但,如果你无需针对 map key 作深度的值的操作、转换,那么你可以直接使用 interface{}
状态的 k
而不必一定要对其进行拆箱操作。

取得 map 的 key,value 类型

kt, vt := typ.Key(), typ.Elem()

通过 typ.Key()
, typ.Elem()
可以取得 map 对象所被定义的 k, v 类型。例如对于 map[string]bool
来说, typ.Key()
将会为 reflect.String
typ.Elem()
将会为 reflect.Int

取得 map 的值

通过 vMap.MapIndex(key)
可以取得 map[key]。

mi := map[string]string{
"a": "this is a",
"b": "this is b",
}
m := reflect.ValueOf(mi)
var key = reflect.ValueOf("a")
val := m.MapIndex(key)
t.Logf("map[%q] = %q", key.String(), val.String())

对 map 进行 range 迭代

m.MapRange()
可以返回一个 *reflect.MapIter
对象。

此对象具有一个成员方法 Next()
,其效果等价于对 map 对象实例的 range 迭代:

var it *reflect.MapIter
it = m.MapRange()
t.Log("iterating...")
for it.Next() {
t.Logf("  m[%q] = %q", it.Key(), it.Value())
}

复制/设置 Map 对象 / 用 (k,v) 对 map 赋值

通过 reflect.SetMapIndex(k, v)
我们可以对 map 进行赋值设置。它相当于 m1[k1] = v1
这样的操作。

此时,m1 = m.Interface(), k1 = k.Interface(), v1 = v.Interface()。请注意这只是一个简略的表示,因为对于基本类型来说,k.Interface() 是无意义的,但我们的意图在于用一个 Value 对象,也就是 k,的 Interface() 方法来表示 k 这个 Value 对象所表示的实际数值,对于 int 来说它其实应该是 k.Int(),对于 float64 来说它其实应该是 k.Float(),但为了叙述文字的简单性,我们统一用 k.Interface() 调用来指示我们想要 Value 对象的底层数值。

func testMap_basics(t *testing.T) {
mi := map[string]string{
"a": "this is a",
"b": "this is b",
}
var input interface{}
input = mi
m := reflect.ValueOf(input)
if m.Kind() == reflect.Map {
newInstance := reflect.MakeMap(m.Type())
keys := m.MapKeys()
for _, k := range keys {
key := k.Convert(newInstance.Type().Key())
value := m.MapIndex(key)
newInstance.SetMapIndex(key, value)
}
t.Logf("newInstance = %v", newInstance)
}
}

从零开始构造 map 新实例

一个完整示例

如果想要从零开始构造 map 新实例,可以参考下面的例子(通过 reflect.MapOf):

func testMap_newMap_1(t *testing.T) {
var key = "key1"
var value = 123
var keyType = reflect.TypeOf(key)
var valueType = reflect.TypeOf(value)
var aMapType = reflect.MapOf(keyType, valueType)
aMap := reflect.MakeMapWithSize(aMapType, 0)
aMap.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(value))
t.Logf("%T:  %v\n", aMap.Interface(), aMap.Interface())
}

更进一步的完整示例

func testMap_newMap_2(t *testing.T) {
key := 1
value := "abc"
mapType := reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(value))
mapValue := reflect.MakeMap(mapType)
mapValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(value))
mapValue.SetMapIndex(reflect.ValueOf(2), reflect.ValueOf("def"))
mapValue.SetMapIndex(reflect.ValueOf(3), reflect.ValueOf("gh"))
keys := mapValue.MapKeys()
for _, k := range keys {
ck := k.Convert(mapValue.Type().Key())
cv := mapValue.MapIndex(ck)
t.Logf("key: %v,  value: %v", ck, cv)
}
}

:end:

微信扫一扫,分享到朋友圈

Golang Reflect 系列二 – 和 Map 操作有关的

Egg.js+MongoDB+uni-app 开发小程序 (一) 项目后台基搭建

上一篇

SpaceX星际飞船原型SN8最早9日进行首次高空试飞

下一篇

你也可能喜欢

Golang Reflect 系列二 – 和 Map 操作有关的

长按储存图像,分享给朋友