go反射使用及proto协议字段随机动态赋值

发布时间 2023-07-01 22:04:10作者: 零下¥六度

1. 基本概念

  Go 语言的反射是一种在运行时动态访问程序元数据的能力。反射可以让我们在运行时检查类型和变量,例如它的大小、方法和动态的值等。这种机制让我们可以编写更加通用的函数和类型,而不需要关心具体的类型。

  在 Go 语言中,反射的实现主要依赖于两种类型:TypeValue。这两种类型都定义在 reflect 包中。  

  • Type:代表 Go 语言中的类型。通过 Type,我们可以获取类型的名称、种类、包路径、大小、是否为指针类型、是否为接口类型、是否可导出等信息。我们可以通过 reflect.TypeOf 函数获取一个值的 Type
  • Value:代表 Go 语言中的值。通过 Value,我们可以获取值的类型、种类、是否可导出、是否可寻址、是否可修改等信息。我们还可以通过 Value 修改值、调用方法等。我们可以通过 reflect.ValueOf 函数获取一个值的 Value

  在 Go 语言的反射机制中,还有一个重要的概念是种类(Kind)。每个 Type 都有一个对应的种类,例如 intfloat64structpointer 等。我们可以通过 Type.Kind 方法获取一个类型的种类。

  Go 语言的反射机制虽然强大,但也有一些限制。例如,我们不能通过反射创建一个接口值的具体值,也不能获取一个非导出字段的 Value。此外,使用反射会有一定的性能开销,因此在编写性能敏感的代码时,应尽量避免使用反射。

  总的来说,Go 语言的反射是通过 TypeValue 这两种类型,以及种类(Kind)这个概念来实现的。通过反射,我们可以在运行时动态地访问和修改程序的元数据。

2. 接口

  a) reflect.TypeOf(xxx)

  b) reflect.ValueOf(xxx)

  c) reflect.New(xxx)

  返回的都是指针,如果需要获取对象需要通过 ret.Elem() 获取

3. 用例

//随机给proto协议对象赋值
func randMsg() (int32, proto.Message) {
    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)
        }
    }
}

4. 总结:

   指针与对象转换 Kind(),Elem(),Addr(),CanSet(),Name()接口,要求区分返回的到底是指针还是对象实例