1. 基本概念
- Go 语言的反射是一种在运行时动态访问程序元数据的能力。反射可以让我们在运行时检查类型和变量,例如它的大小、方法和动态的值等。这种机制让我们可以编写更加通用的函数和类型,而不需要关心具体的类型。
- 在 Go 语言中,反射的实现主要依赖于两种类型:
Type
和Value
。这两种类型都定义在reflect
包中。Type
:代表 Go 语言中的类型。通过Type
,我们可以获取类型的名称、种类、包路径、大小、是否为指针类型、是否为接口类型、是否可导出等信息。我们可以通过reflect.TypeOf
函数获取一个值的Type
。Value
:代表 Go 语言中的值。通过Value
,我们可以获取值的类型、种类、是否可导出、是否可寻址、是否可修改等信息。我们还可以通过Value
修改值、调用方法等。我们可以通过reflect.ValueOf
函数获取一个值的Value
。
- 在 Go 语言的反射机制中,还有一个重要的概念是种类(Kind)。每个
Type
都有一个对应的种类,例如int
、float64
、struct
、pointer
等。我们可以通过Type.Kind
方法获取一个类型的种类。 - Go 语言的反射机制虽然强大,但也有一些限制。例如,我们不能通过反射创建一个接口值的具体值,也不能获取一个非导出字段的
Value
。此外,使用反射会有一定的性能开销,因此在编写性能敏感的代码时,应尽量避免使用反射。 - 总的来说,Go 语言的反射是通过
Type
和Value
这两种类型,以及种类(Kind)这个概念来实现的。通过反射,我们可以在运行时动态地访问和修改程序的元数据。
2. 接口
a) reflect.TypeOf(xxx)
b) reflect.ValueOf(xxx)
c) reflect.New(xxx)
返回的都是指针,如果需要获取对象需要通过 ret.Elem() 获取
3. 用例
//随机给proto协议对象赋值var msgId int32 if common.Random(0, 4) == 0 { //25%的概率发送ack or ntf协议 var msgIds []int32 for id, name := range pb_server.MessageID_name { if strings.Contains(name, "ACK") || strings.Contains(name, "NTF") { msgIds = append(msgIds, id) } } msgId = msgIds[common.Random(0, len(msgIds))] } else { //75%的概率发送req协议 var msgIds []int32 for id, name := range pb_server.MessageID_name { if strings.Contains(name, "REQ") { msgIds = append(msgIds, id) } } msgId = msgIds[common.Random(0, len(msgIds))] } //msgId = 1064 //slice test //msgId = 1111 //map test proname := "server." + common.ProtoMsgTrans(pb_server.MessageID_name[msgId]) obj, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(proname)) if err != nil { l4g.Debug("RandMsgEvent name:%s err:%v", proname, err) return 0, nil } msg := proto.MessageV1(obj.New()) l4g.Debug("RandMsgEvent name:%s", proname) // 获取 Message 的反射类型和值 msgType := reflect.TypeOf(msg).Elem() msgValue := reflect.ValueOf(msg).Elem() foreachField(msgType, &msgValue) return msgId, msg } func foreachField(tp reflect.Type, value *reflect.Value) { for i := 0; i < tp.NumField(); i++ { field := tp.Field(i) if strings.HasPrefix(field.Name, "XXX_") { continue } //l4g.Debug("forechField i:%d name:%s type:%v", i, field.Name, field.Type.Kind().String()) fieldValue := value.Field(i) setValue(&field, &fieldValue) } } func setValue(field *reflect.StructField, value *reflect.Value) { switch field.Type.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: { var tmp int64 switch field.Type.Kind() { case reflect.Int: tmp = int64(math.MaxInt) case reflect.Int8: tmp = int64(math.MaxInt8) case reflect.Int16: tmp = int64(math.MaxInt16) case reflect.Int32: tmp = int64(math.MaxInt32) case reflect.Int64: tmp = int64(math.MaxInt64) } rand := common.Random(0, 3) if rand == 0 { value.SetInt(int64(0)) } else if rand == 1 { value.SetInt(int64(common.Random(0, int(tmp)))) } else { value.SetInt(tmp) } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: { var tmp uint64 switch field.Type.Kind() { case reflect.Uint: tmp = uint64(math.MaxUint) case reflect.Uint8: tmp = uint64(math.MaxUint8) case reflect.Uint16: tmp = uint64(math.MaxUint16) case reflect.Uint32: tmp = uint64(math.MaxUint32) case reflect.Uint64: tmp = uint64(math.MaxUint64) } rand := common.Random(0, 3) if rand == 0 { value.SetUint(uint64(0)) } else if rand == 1 { value.SetUint(uint64(common.Random(0, int(tmp)))) //tmp越界忽略 } else { value.SetUint(tmp) } } case reflect.String: { value.SetString("hello world!!!") } case reflect.Float32, reflect.Float64: { value.SetFloat(float64(common.Random(0, 100))) } case reflect.Bool: { value.SetBool(common.Random(0, 2) == 0) } case reflect.Ptr: { newType := field.Type.Elem() newVal := reflect.New(newType).Elem() foreachField(newType, &newVal) value.Set(newVal.Addr()) } case reflect.Struct: //proto生成的代码结构都是指针,无需实现 case reflect.Slice: { sliceType := field.Type sliceLen := common.Random(1, 6) // 随机生成长度为 1 到 5 的切片 newSlice := reflect.MakeSlice(sliceType, sliceLen, sliceLen) elemType := sliceType.Elem() for i := 0; i < sliceLen; i++ { elem := newSlice.Index(i) if elem.Type().Kind() == reflect.Ptr { newElem := reflect.New(elem.Type().Elem()).Elem() foreachField(elemType.Elem(), &newElem) elem.Set(newElem.Addr()) } else { elemField := &reflect.StructField{ Name: field.Name, PkgPath: field.PkgPath, Type: elemType, Tag: field.Tag, Offset: field.Offset, Index: field.Index, Anonymous: field.Anonymous, } setValue(elemField, &elem) } newSlice.Index(i).Set(elem) } value.Set(newSlice) } case reflect.Map: { mapType := field.Type mapLen := common.Random(1, 6) // 随机生成长度为 1 到 5 的映射 newMap := reflect.MakeMapWithSize(mapType, mapLen) keyType := mapType.Key() elemType := mapType.Elem() fmt.Println("map type:", mapType.Kind(), "elemType:", elemType.Kind()) for i := 0; i < mapLen; i++ { //key newKey := reflect.New(keyType).Elem() keyField := reflect.StructField{ Name: field.Name + "_key", PkgPath: field.PkgPath, Type: keyType, Tag: field.Tag, Offset: field.Offset, Index: field.Index, Anonymous: field.Anonymous, } setValue(&keyField, &newKey) //value newElem := reflect.New(elemType).Elem() //fmt.Println("newElem kind:", newElem.Kind(), "typename:", newElem.Type().Name()) if elemType.Kind() == reflect.Ptr { tmpElem := reflect.New(newElem.Type().Elem()).Elem() foreachField(elemType.Elem(), &tmpElem) newElem.Set(tmpElem.Addr()) } else if elemType.Kind() == reflect.Struct { foreachField(elemType, &newElem) } else { elemField := reflect.StructField{ Name: field.Name + "_value", PkgPath: field.PkgPath, Type: elemType, Tag: field.Tag, Offset: field.Offset, Index: field.Index, Anonymous: field.Anonymous, } setValue(&elemField, &newElem) } newMap.SetMapIndex(newKey, newElem) } value.Set(newMap) } default: { l4g.Error("setFieldValue unkown kind:%v name:%s", field.Type.Kind(), field.Name) } } }
func randMsg() (int32, proto.Message) {
4. 总结:
指针与对象转换 Kind(),Elem(),Addr(),CanSet(),Name()接口,要求区分返回的到底是指针还是对象实例
标签:case,tmp,proto,value,reflect,field,go,Type,赋值 From: https://www.cnblogs.com/Lucky-qin2013/p/17513259.html