Go反射四讲---第四讲:如何使用反射遍历字符串,切片,数组和Map呢?

反射-遍历

这是我们反射四讲的第四讲,本次给大家讲解如何使用反射处理一些简单的遍历。

这一讲非常的简单,主要介绍如何遍历数组,切片,字符串,map。

其中,前三种逻辑一致,在遍历map时会有些许不同,不过不用担心,还是很简单的。

数组&切片&字符串

// Iterate 遍历数组,切片,或者字符串
func Iterate(input any) ([]any, error) {
	val := reflect.ValueOf(input)
	typ := val.Type()
	kind := typ.Kind()

	if kind != reflect.Array && kind != reflect.Slice && kind != reflect.String {
		return nil, errors.New("input must be array, slice or string")
	}
	res := make([]any, 0, val.Len())
	for i := 0; i < val.Len(); i++ {
		ele := val.Index(i)
		if kind == reflect.String {
			res = append(res, string(ele.Interface().(uint8)))
		} else {
			res = append(res, ele.Interface())
		}
	}
	return res, nil
}

测试:

func TestIterate(t *testing.T) {
	testcases := []struct {
		name    string
		input   any
		wantRes []any
		wantErr error
	}{
		{
			name:    "slicex",
			input:   []int{1, 2, 3},
			wantRes: []any{1, 2, 3},
		},
		{
			name:    "array",
			input:   [3]int{1, 2, 3},
			wantRes: []any{1, 2, 3},
		},
		{
			name:    "string",
			input:   "hello",
			wantRes: []any{"h", "e", "l", "l", "o"},
		},
		{
			name:    "invalid",
			input:   map[string]int{"a": 1},
			wantErr: errors.New("input must be array, slicex or string"),
		},
	}
	for _, tt := range testcases {
		t.Run(tt.name, func(t *testing.T) {
			res, err := Iterate(tt.input)
			assert.Equal(t, err, tt.wantErr)
			assert.Equal(t, tt.wantRes, res)
		})
	}
}

Map

在遍历 Map 时,为大家提供两种实现。
第一种:使用 MapRange 提供的迭代器进行遍历。

// IterateMapV1 遍历map
func IterateMapV1(input any) ([]any, []any, error) {
	val := reflect.ValueOf(input)
	if val.Kind() != reflect.Map {
		return nil, nil, errors.New("input must be map")
	}
	l := val.Len()
	keys := make([]any, 0, l)
	values := make([]any, 0, l)
	itr := val.MapRange()
	for itr.Next() {
		keys = append(keys, itr.Key().Interface())
		values = append(values, itr.Value().Interface())
	}
	return keys, values, nil
}

第二种:使用 MapKeys 提供的 keys 切片进行遍历。

// IterateMapV2 遍历map
func IterateMapV2(input any) ([]any, []any, error) {
	val := reflect.ValueOf(input)
	if val.Kind() != reflect.Map {
		return nil, nil, errors.New("input must be map")
	}
	l := val.Len()
	keys := make([]any, 0, l)
	values := make([]any, 0, l)
	for _, key := range val.MapKeys() {
		keys = append(keys, key.Interface())
		v := val.MapIndex(key)
		values = append(values, v.Interface())
	}
	return keys, values, nil
}

测试:

func TestIterateMap(t *testing.T) {
	testcases := []struct {
		name       string
		input      any
		wantKeys   []any
		wantValues []any
		wantErr    error
	}{
		{
			name:    "nil",
			input:   nil,
			wantErr: errors.New("input must be map"),
		},
		{
			name:       "map",
			input:      map[string]int{"a": 1, "b": 2, "c": 3},
			wantKeys:   []any{"a", "b", "c"},
			wantValues: []any{1, 2, 3},
		},
	}
	for _, tt := range testcases {
		t.Run(tt.name, func(t *testing.T) {
			keys, values, err := IterateMapV2(tt.input)
			assert.Equal(t, err, tt.wantErr)
			assert.Equal(t, tt.wantKeys, keys)
			assert.Equal(t, tt.wantValues, values)
		})
	}
}

反射总结

反射可以让我们去设计一些通用型的代码以及处理方式,并且提供了一种灵活的让程序去自己了解自己和改变自己的能力。但是,反射也有极大的缺点,有时候可能会导致程序崩溃,并且反射代码相对苦涩难懂,不易于理解等。

反射四讲总结

至此,反射四讲完美结束,我平时很喜欢使用 TDD 的流程去开发,所以为大家提供了测试程序。

如果大家想要有一个小项目去实战练习的话,我给大家推荐我之前在ORM层面上实现的元数据的操作,同时也可以帮你练习代码能力。这是项目地址:ORM元数据的设计

最后,总结一下,没有足够的测试,我们一般不要使用反射,因为反射充满了panic

很感谢大家的观看,如果您有宝贵的建议或者想要去了解哪一方面得知识,我很高兴为您讲解。

结束,下期见。

你可能感兴趣的:(Go,golang,算法,开发语言,后端,微服务,服务发现)