英文:
Switch on interface type without type assertion
问题
我有一个函数,可以接受多种不同类型的参数。我想使用类型开关(type switch)来尽量减少代码重复。作为一个非常基本的例子,我想将uint8
和int8
类型都复制到一个字节缓冲区中。下面的代码可以正常工作:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论