go: 反射小实例 这篇文章将搜集一些平时在工作中用到的使用反射小实例。 反射反射真快乐。 ## 遍历slice 本实例将遍历slice,并且将它转换为int类型 ``` func Slice2Ints(s interface{}) ([]int, error) { val := reflect.ValueOf(s) if val.Kind() != reflect.Slice { return nil, fmt.Errorf("s:%v is not slice type", s) } ret := make([]int, 0, 10) for i := 0; i < val.Len(); i++ { e := val.Index(i) v := e.Interface() intV, err := ToInt(v) // 这里自己实现 if err != nil { return nil, err } ret = append(ret, intV) } return ret, nil } ``` ## 读取map 本实例从map中取一个随机的key ``` func getRndKey(m interface{}) (string, error) { val := reflect.ValueOf(m) if val.Kind() != reflect.Map { return "", fmt.Errorf("%v is not map type", m) } it := val.MapRange() isok := it.Next() if isok { k := it.Key() v, ok := k.Interface().(string) if !ok { return "", fmt.Errorf("map key:%v is not string", k) } return v, nil } return "", fmt.Errorf("empty map") } ``` ## 从channel中读数据 ``` func main() { ch := make(chan int, 10) ch <- 1 ch <- 2 ch <- 3 ctx, _ := context.WithTimeout(context.Background(), time.Second) ReadChan(ctx, ch, func(i interface{}) bool { log.Println(i) return true }) } // ReadChan 当channel被关闭或者ctx done,函数退出,否则卡住等待channel中的消息 func ReadChan(ctx context.Context, ch interface{}, fn func(i interface{}) bool) { val := reflect.ValueOf(ch) if val.Kind() != reflect.Chan { return } cases := make([]reflect.SelectCase, 2) cases[0] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: val} cases[1] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ctx.Done())} for { chosen, v, ok := reflect.Select(cases) if chosen == 1 { break } if !ok { break } ret := fn(v.Interface()) if !ret { return } } } ``` ## 从结构体中的tag中取默认值,并设置到结构体 假设结构体有一个default的tag,如何用它的值作为字段的默认值? ``` func main() { got := &TestStruct{} err := SetDefault(got) if err != nil { logrus.Error(err) return } logrus.Infof("got:%v", got) } type TestStruct struct { WillSetInt int `default:"100"` WillSetString string `json:"will_set_string" default:"superpig"` CannotSet int `json:"cannot_set"` } // 从tag default中取值,然后set进去,只实现了int, string类型 func SetDefault(structVal interface{}) error { ttype := reflect.TypeOf(structVal) if ttype.Kind() != reflect.Ptr { return fmt.Errorf("input:%v is not ptr", ttype.Kind()) } ttype = ttype.Elem() if ttype.Kind() != reflect.Struct { return fmt.Errorf("input:%v is not struct", ttype.Kind()) } val := reflect.ValueOf(structVal).Elem() for i := 0; i < ttype.NumField(); i++ { tag := ttype.Field(i).Tag.Get("default") if tag == "" { continue } switch val.Field(i).Kind() { case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64: if val.Field(i).Int() != 0 { continue } v, err := strconv.Atoi(tag) if err != nil { return fmt.Errorf("field:%v, tag:%v cannot convert to int", ttype.Field(i).Name, tag) } val.Field(i).SetInt(int64(v)) case reflect.String: if val.Field(i).String() != "" { continue } val.Field(i).SetString(tag) default: return fmt.Errorf("field:%v kind:%v is not supported", ttype.Field(i).Name, val.Field(i).Kind()) } } return nil } ``` ## 创建slice或者数组对象并为它设置值 创建slice非常简单,系统自带了MakeSlice。 ``` snew := reflect.MakeSlice(ttype, length, cap) for i := 0; i < length; i++ { newV:= reflect.ValueOf("abcdef") snew.Index(i).Set(newV) } ``` 创建Array则复杂一点,需要先使用ArrayOf得到数组的类型,再使用reflect.New。 最重要的,生成后的类型需要使用.Elem才能赋值。 ``` snew := reflect.New(reflect.ArrayOf(length, reflect.TypeOf(int(0))).Elem() for i := 0; i < length; i++ { newV := reflect.ValueOf(int(123)) snew.Index(i).Set(newV) } ``` 来自 大脸猪 写于 2021-09-09 11:29 -- 更新于2023-12-19 15:16 -- 0 条评论