zoukankan      html  css  js  c++  java
  • go反射实例

     需求分析:

    如在rocketmq的网络通信中,所有通信数据包以如下形式传输: (注:rocketmq的java结构体,这里使用了go形式表示)

    type RemotingCommand struct {
    	//header
    	Code      int               `json:"code"`
    	Language  string            `json:"language"`
    	Version   int               `json:"version"`
    	Opaque    int32             `json:"opaque"`
    	Flag      int               `json:"flag"`
    	Remark    string            `json:"remark,omitempty"`
    	ExtFields map[string]string `json:"extFields"`
    	//body
    	Body []byte `json:"body,omitempty"`
    }

    其中,ExtFields 表示用户自定义数据包,如:在某次通信中传输的 ExtFields 的内容如下,接收对象为 MyResponseHeader 型。

    //ExtFields 数据内容
    extFields := make(map[string]string)
    extFields ["result"] = "true"
    extFields ["answer"] = "1234"
    
    //MyResopnseHeader 接口体
    type MyResponseHeader struct {
    	Result bool
    	Answer int64
    }

    将 “extFields ” 转化为 MyResponseHeader 型过程中,需要将string型数据分别转换为 bool、int64等类型。

    另外,不同的remotingCommand包接收到数据后需要解析成不同的结构体数据, 如何使用一个方式与统一解析数据呢? 解决这个问题需要用到反射。本文根据这个问题,对go中的反射知识进行了简单实践,具体内容如下:

    涉及到的反射知识点补充

    1.reflect.Value

    reflect.ValueOf()的返回值类型为reflect.Value,表示值的真实内容。

    var i int = 123
    var s = "abc"
    fmt.Println(reflect.ValueOf(i)) // 123
    fmt.Println(reflect.ValueOf(s)) // abc

    2.reflect.Value值的设置

    go中不能直接对Value进行赋值操作,如对上述变量 s 进行赋值,首先需要拿到 s 值的指针,然后拿到该指针的reflect.Value,指针的reflect.Value调用Value.Elem()后对对应到 s 值对象,继而可以对 s 进行赋值操作。

    value赋值的例子:

    func main(){
        var i int = 123
        fe := reflect.ValueOf(&i).Elem()  //必须是指针的Value才能调用Elem
        fmt.Println(fe)         // 123
        fmt.Println(fe.CanSet()) // true
        fe.SetInt(456)
        fmt.Println(i) //456
    }

    3.reflect.Type.Kind 与 reflect.Value.Kind

    它返回的是对象的基本类型,例如 Float32、Float64、int32、int64、Slice、Bool、Complex64、Array、chan、Func、Interface、Map 等等。

    4.reflect.Type.Filed 与 relfect.Type.Filed

    前者放回的是一个StructFiled对象。后者返回的还是一个Value对象

    type StructField struct {
        Name string         // name 常用
        PkgPath string
    
        Type      Type      // field type 常用
        Tag       StructTag // field tag string
        Offset    uintptr   // offset within struct, in bytes
        Index     []int     // index sequence for Type.FieldByIndex
        Anonymous bool      // is an embedded field
    }

    5.reflect.Type.FiledByName 与 reflect.Value.FileByName

    前者返回一个StructFiled对象,后者返回的还是一个Value对象

     

    具体实现过程

    参考rocketmq的思路,先定义一个 CustomHeader接口,自定义包实现该接口,然后定义一个解析包的方法,该方法中包括go反射的运用。

    自定义数据包:

    type CustomHeader interface {
        CheckFields() error
    }

    结构体实现了 CustomHeader 接口:

    type MyResponseHeader struct {
    	Result bool
    	Answer int64
    }
    
    func (t *MyResponseHeader) CheckFields() error {
    	return nil
    }

    转化测试:

    //ExtFields 数据内容
    extFields := make(map[string]string)
    extFields ["result"] = "true"
    extFields ["answer"] = "1234"
    err := DecodeCustomHeader(extFields, myResponseHeader)
    if err != nil {
      panic(err.Error())
    }
    fmt.Printf("myResponseHeader.Result = %v  
    myResponseHeader.Answer = %d
    ", myResponseHeader.Result, myResponseHeader.Answer) 

    结果:

    myResponseHeader.Result = true  
    myResponseHeader.Answer = 1234

    DecodeCustomHeader代码如下:

    func DecodeCustomHeader(extFields map[string]string, commandCustomHeader CustomHeader) error {
        structValue := reflect.ValueOf(commandCustomHeader).Elem()
    
        for k, v := range extFields {
            err := reflectSturctSetField(structValue, firstLetterToUpper(k), v)
            if err != nil {
                return err
            }
        }
    
        return nil
    }
    
    // 支持string int8 int16 int int32 int64 uint8 uint16 uint32 uint64 bool,非string类型将进行转换
    func reflectSturctSetField(structValue reflect.Value, name string, value string) error {
        structFieldValue := structValue.FieldByName(name)
    
        if !structFieldValue.IsValid() {
            return errors.Errorf("No such field: %s in obj", name)
        }
    
        if !structFieldValue.CanSet() {
            return errors.Errorf("Cannot set %s field value", name)
        }
    
        structFieldType := structFieldValue.Type()
        switch structFieldType.Kind() {
        case reflect.String:
            structFieldValue.SetString(value)
        case reflect.Int8:
            fallthrough
        case reflect.Int16:
            fallthrough
        case reflect.Int32:
            fallthrough
        case reflect.Int64:
            fallthrough
        case reflect.Int:
            ival, err := strconv.ParseInt(value, 10, 64)
            if err != nil {
                return errors.Wrap(err, 0)
            }
            structFieldValue.SetInt(ival)
        case reflect.Uint8:
            fallthrough
        case reflect.Uint16:
            fallthrough
        case reflect.Uint32:
            fallthrough
        case reflect.Uint64:
            ival, err := strconv.ParseUint(value, 10, 64)
            if err != nil {
                return errors.Wrap(err, 0)
            }
            structFieldValue.SetUint(ival)
        case reflect.Bool:
            bval, err := strconv.ParseBool(value)
            if err != nil {
                return errors.Wrap(err, 0)
            }
            structFieldValue.SetBool(bval)
        default:
            return errors.Errorf("Provided value type didn't match obj field type")
        }
    
        return nil
    }
    
    // 首字母大写
    func firstLetterToUpper(s string) string {
        if len(s) > 0 {
            b := []byte(s)
            if b[0] >= 'a' && b[0] <= 'z' {
                b[0] = b[0] - byte(32)
                s = string(b)
            }
        }
    
        return s
    }

    只要实现了CustomHeader接口的结构体,调用DecodeCustomHeader方法,就可以获取对应的数据了。另外根据业务需要,可以扩展 reflectSturctSetField 方法。(完)

  • 相关阅读:
    ubuntu 安装 redis desktop manager
    ubuntu 升级内核
    Ubuntu 内核升级,导致无法正常启动
    spring mvc 上传文件,但是接收到文件后发现文件变大,且文件打不开(multipartfile)
    angular5 open modal
    POJ 1426 Find the Multiple(二维DP)
    POJ 3093 Margritas
    POJ 3260 The Fewest Coins
    POJ 1837 Balance(二维DP)
    POJ 1337 A Lazy Worker
  • 原文地址:https://www.cnblogs.com/chenjunjie12321/p/7889797.html
Copyright © 2011-2022 走看看