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 方法。(完)

  • 相关阅读:
    android handle详解
    android面试详解
    linux网络编程-一个简单的线程池(41)
    linux网络编程-posix条件变量(40)
    如何写一个简单的分页
    jQuery 3 有哪些新东西
    浅析正则表达式模式匹配的 String 方法
    jQuery源码浅析2–奇技淫巧
    前端文本截断
    你会用setTimeout吗
  • 原文地址:https://www.cnblogs.com/chenjunjie12321/p/7889797.html
Copyright © 2011-2022 走看看