29

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

 4 years ago
source link: https://hedzr.github.io/golang/reflect/golang-reflect-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

通过反射方式对 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 表示。由于它自动包含了 & 取地址操作,因为得到的 newInstanceValueaddressable 的,这意味着你可以直接使用 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.Stringtyp.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:


Recommend

  • 75

    reflect action

  • 141

    Hash Stable Pack This is a code generation tool for QUICK struct content compare or hash computation. For Quick compare nested struct without reflection...

  • 37

    了解和使用golang有一段时间了,由于项目比较赶,基本是现学现卖的节奏。最近有时间会在简书上记录遇到的一些问题和解决方案,希望可以一起交流探讨。 需求 在golang中,给定一组数据,例如 map[string]in...

  • 52

    前言 interface (即接口),是Go语言中一个重要的概念和知识点,而功能强大的 reflect 正是基于 interface 。本文即是对Go语言中的 interface 和 reflect

  • 47
    • segmentfault.com 5 years ago
    • Cache

    golang reflect包,反射学习与实践

    Go 语言反射的三大法则,其中包括: 从 interface{} 变量可以反射出反射对象; 从反射对象可以获取 interface{} 变量; 要修改反射对象,其值必须可设置; 从反射对象到接口值的过程就是...

  • 29
    • studygolang.com 4 years ago
    • Cache

    Golang标准库——reflect

    reflect reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表...

  • 14
    • blog.csdn.net 4 years ago
    • Cache

    golang 用反射reflect操作结构体

    golang 用反射reflect操作结构体

  • 6
    • dinghaoli.github.io 3 years ago
    • Cache

    Golang反射reflect的深入理解

    本文参考: Golang的反射reflect深入理解和示例 golang: 详解interface和nil 编程语言中反射的...

  • 7
    • kalifun.github.io 3 years ago
    • Cache

    Golang反射(reflect)

    Golang反射(reflect)大表哥我创建了一个数据库,我想通过一个函数就能实现所有表的插入。那你就用传入不定参数进行数据添加啊。可是我觉得好麻烦哦,我还得分析它的类型符不符合,它属于哪个表的数据。这样太费劲了,我还不如直接...

  • 9
    • justinyan.me 3 years ago
    • Cache

    Golang的反射(reflect)

    0. Golang struct 声明时的特殊标记 用 Golang 肯定见过类似这样的标记: type Model struct { ID...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK