如何使这段基于GO的代码更简洁?

huangapple go评论83阅读模式
英文:

How can I make this reflect based GO code simpler?

问题

我正在使用一种非常复杂的协议,结合了ASN和一种XDR的变体以及其他编码方式,对一个相当复杂的结构进行编码。

我基于GitHub上可用的xdr编码器实现了代码。代码是基于反射的,并且它能够工作,但我不喜欢我实现目标类型切换的方式:

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

我认为以下方法可能更好,但我无法使其正常工作:

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

它无法正常工作的原因是ve是相同的反射类型,而不是目标类型。

以下函数提供了代码的完整上下文:

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\n", st, ve.Kind())
	}

	// 处理原生的Go类型。
	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
	}

	// 剩下的未处理类型都是不支持的。在撰写本文时,唯一剩下的不支持的类型是reflect.Uintptr和reflect.UnsafePointer。
	enc.err = errors.New(fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String()))
}

如果你知道一个更好的例子,可以更好地根据类型和种类进行切换,请告诉我。

谢谢。

更新:

阅读了解决方案后,我调整了一个可以工作的变体:

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\n", st, ve.Kind())
}
英文:

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\n", 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\n", st, ve.Kind())
}

答案1

得分: 4

使用type switch来处理底层值

switch v := ve.Interface().(type) {
case time.Time:
    log.Println("处理 time.Time")
    enc.encodeTime(v)
    return
case []byte:
    log.Println("处理 []uint8")
    enc.writeOctetString(v)
    return
case byte:
    enc.writeUint8(v)
    return
// ... 还可以添加更多类型
default:
    log.Printf("处理类型:%v,种类:%v\n", ve.Type(), ve.Kind())
}

playground 示例

你也可以根据reflect.Type进行切换,而不是使用字符串:

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

playground 示例

英文:

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\n", 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

huangapple
  • 本文由 发表于 2015年10月28日 08:09:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/33380708.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定