douyue3800 2015-10-28 00:09
浏览 13
已采纳

如何使基于反射的GO代码更简单?

I am encoding a rather complex structure using a very complicated protocol that's a mix of ASN and a variant of XDR and other encodings.

I based the implementation on xdr encoder available on github. The code is reflection based and it works, but I don't like how I implemented target type switch:

st := ve.Type().String()
    switch st {
    case "time.Time":

I think the following approach might be better, but I could not get it to work properly:

switch ve.(type) {
case time.Time:

The reason it does not work is that ve is of the same reflection type and not the target type.

The following function provides full context of the code:

func (enc *encoderState) encode(v reflect.Value) {

ve := enc.indirect(v)

st := ve.Type().String()
switch st {
case "time.Time":
    log.Println("Handling time.Time")
    t, ok := ve.Interface().(time.Time)
    if !ok {
        enc.err = errors.New("Failed to type assert to time.Time")
        return
    }
    enc.encodeTime(t)
    return
case "[]uint8":
    log.Println("Handling []uint8")
    enc.writeOctetString(ve.Bytes())
    return
default:
    log.Printf("Handling type: %v by kind: %v
", st, ve.Kind())
}

// Handle native Go types.
switch ve.Kind() {
case reflect.Uint8: // , reflect.Int8
    enc.writeUint8(uint8(v.Uint()))
    return
case reflect.Uint16: // , reflect.Int16
    enc.writeUint16(uint16(v.Uint()))
    return
case reflect.Bool:
    enc.writeBool(ve.Bool())
    return
case reflect.Struct:
    enc.encodeStruct(ve)
    return
case reflect.Interface:
    enc.encodeInterface(ve)
    return
}

// The only unhandled types left are unsupported.  At the time of this
// writing the only remaining unsupported types that exist are
// reflect.Uintptr and reflect.UnsafePointer.
enc.err = errors.New(fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String()))

}

If you know of a better example that can switch by type and kind better, please, let me know.

Thank you

UPDATE

After reading the solution I adjusted to a variation that works:

vi := ve.Interface()
switch st := vi.(type) {
case time.Time:
    enc.encodeTime(vi.(time.Time))
    return
case []uint8:
    enc.writeOctetString(vi.([]byte))
    return
default:
    log.Printf("Handling type: %v by kind: %v
", st, ve.Kind())
}
  • 写回答

1条回答 默认 最新

  • duanshan188866 2015-10-28 01:17
    关注

    Use a type switch on the underlying value:

    switch v := ve.Interface().(type) {
    case time.Time:
        log.Println("Handling time.Time")
        enc.encodeTime(v)
        return
    case []byte:
        log.Println("Handling []uint8")
        enc.writeOctetString(v)
        return
    case byte:
        enc.writeUint8(v)
        return
    // ... and more types here
    default:
        log.Printf("Handling type: %v by kind: %v
    ", ve.Type(), ve.Kind())
    }
    

    playground example

    You can also switch on the reflect.Type instead of the string:

    switch ve.Type() {
    case reflect.TypeOf(time.Time{}):
        log.Println("Handling time.Time")
        ...
    case reflect.TypeOf([]byte{}):
        log.Println("Handling []uint8")
        ...
    case reflect.TypeOf(uint8(0)):
        ...
    }
    

    playground example

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。