【问题标题】:Switch on interface type without type assertion打开没有类型断言的接口类型
【发布时间】:2021-12-05 20:15:33
【问题描述】:

我有一个可以接受多种不同参数类型的函数。我想使用类型开关并尽可能减少代码重复。作为一个非常基本的示例,这里我想将uint8int8 类型都复制到一个字节缓冲区中。这段代码可以正常工作

package main

func switchFn(args ...interface{}) {
    var buf []byte
    for _, arg := range args {
        switch val := arg.(type) {
        case uint8:
            buf = append(buf, byte(val))
        case int8:
            buf = append(buf, byte(val))
        }
    }
}

func main() {
    switchFn(int8(42), uint8(42)) // etc
}

您会注意到两个 case 语句的作用完全相同!如果我把它们结合起来......

package main

func switchFn(args ...interface{}) {
    var buf []byte
    for _, arg := range args {
        switch val := arg.(type) {
        case uint8, int8:
            buf = append(buf, byte(val))
        }
    }
}

func main() {
    switchFn(int8(42), uint8(42)) // etc
}

我遇到了cannot convert val (type interface {}) to type byte: need type assertion 的问题。但我实际上是在切换类型!啊!

我是否坚持这里的代码重复,还是有更聪明的方法来做到这一点?注意copy into byte buffer是用来说明例子的,我的函数可能在case块中做其他事情。

【问题讨论】:

  • 不,这些是不同的类型,所以需要不同的情况。
  • 为了减少代码重复,将 case 块移动到一个函数中,并从两个 case 中调用该函数。
  • case uint8, int8: 这是在 Go 中匹配多个案例的有效方法吗?在规范中找不到任何提及
  • @DanielFarrell,这作为一个案例是有效的,但因为它可能是任何一种类型,所以值仍然是一个接口

标签: go switch-statement


【解决方案1】:

这是一种避免代码重复的方法,但代价是……嗯,另一种代码重复:

func switchFn(args ...interface{}) {
    var buf []byte
    for _, arg := range args {
        var val byte
        switch v := arg.(type) {
        case uint8:
            val = byte(v)
        case int8:
            val = byte(v)
        default:
            panic("wrong type")
        }
        buf = append(buf, val)
    }
}

对于这个特定的功能,原始副本可能更好。如果buf = append(buf, val) 部分变得更大或更复杂,这可能会更好。

在其他情况下——也许是最真实的情况——gopher suggests 方法可能是最好的:

    f := func(val byte) {
        buffer = append(buffer, val)
    }

您现在可以从每个case 呼叫f

【讨论】:

    【解决方案2】:

    案例可以合并,但val 将在块中具有interface{} 类型。这对您的方案没有用处。

    使用函数减少代码重复。

    func switchFn(args ...interface{}) {
        var buf []byte
    
        byteFn := func(b byte) {
            buf = append(buf, b)
        }
    
        for _, arg := range args {
            switch val := arg.(type) {
            case uint8:
                byteFn(val)
            case int8:
                byteFn(byte(val))
            }
        }
    }
    

    reflect API 无济于事,因为有符号值和无符号值需要单独的代码。 reflect API 有助于将所有有符号整数合并到代码块中,并将所有无符号整数合并到另一个代码块中。

    for _, arg := range args {
        switch val := arg.(type) {
        case int, int8, int16, int32, int64:
            i := reflect.ValueOf(val).Int()
            // i is an int64
            fmt.Println(i)
        case uint, uint8, uint16, uint32, uint64:
            u := reflect.ValueOf(val).Uint()
            // u is an uint64
            fmt.Println(u)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 2021-05-24
      • 2016-10-03
      • 1970-01-01
      • 1970-01-01
      • 2016-08-01
      • 2021-02-10
      • 1970-01-01
      相关资源
      最近更新 更多