在不进行类型断言的情况下切换接口类型。

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

Switch on interface type without type assertion

问题

我有一个函数,可以接受多种不同类型的参数。我想使用类型开关(type switch)来尽量减少代码重复。作为一个非常基本的例子,我想将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)) // 等等
}

你会注意到,这两个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)) // 等等
}

我会遇到一个cannot convert val (type interface {}) to type byte: need type assertion的问题。但是我明明是在根据类型进行切换!烦死了!

我是否被困在这里,必须重复代码,还是有更聪明的方法可以解决这个问题?请注意,上述示例中的"复制到字节缓冲区"只是为了说明问题,我的实际函数可能在case块中做其他事情。

英文:

I have a function that can take a number of different argument types. I'd like to use a type switch and reduce code duplication as much as possible. As a very basic example, here I want to copy both uint8 and int8 types into a byte buffer. This code happily works

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
}

You'll notice both the case statements do exactly the same thing! If I combine them though...

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
}

I run into an issue of cannot convert val (type interface {}) to type byte: need type assertion. But I'm literally switching on the type! Argh!

Am I stuck with the code duplication here, or is there a smarter way to do this? Note that the copying into byte buffer is used to illustrate the example, my function may be doing other things in the case blocks.

答案1

得分: 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 是 int64 类型
        fmt.Println(i)
    case uint, uint8, uint16, uint32, uint64:
        u := reflect.ValueOf(val).Uint()
        // u 是 uint64 类型
        fmt.Println(u)
    }
}
英文:

The cases can be combined, but val will have type interface{} in the block. That's not useful for your scenario.

Use a function to reduce code duplication.

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))
		}
	}
}

The reflect API will not help because separate code is required for the signed and unsigned values. The reflect API is helpful for combining all signed integers into block of code and all unsigned integers into another block of code.

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)
	}
}

答案2

得分: 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建议的方法可能是最好的:

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

现在你可以从每个case中调用f

英文:

Here's a way to avoid the code duplication, at the cost of ... well, a different sort of code duplication:

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)
    }
}

For this particular function, the original duplication is probably better. Should the buf = append(buf, val) section get larger or more complicated, this would probably be better.

In still other cases—perhaps most real ones—the method gopher suggests is probably best:

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

You can now call f from each case.

huangapple
  • 本文由 发表于 2021年10月19日 03:43:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/69621750.html
匿名

发表评论

匿名网友

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

确定