在没有类型断言的情况下打开接口类型
Switch on interface type without type assertion
我有一个函数可以接受多种不同的参数类型。我想使用类型开关并尽可能减少代码重复。作为一个非常基本的示例,在这里我想将 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)) // 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
的问题。但我真的在打开类型!啊!
我是否坚持这里的代码重复,或者是否有更聪明的方法来做到这一点?请注意,复制到字节缓冲区 用于说明示例,我的函数可能在 case 块中做其他事情。
这是一种避免代码重复的方法,代价是……好吧,另一种代码重复:
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)
部分变得更大或更复杂,这可能会更好。
在其他情况下——也许是最真实的情况下——方法 可能是最好的:
f := func(val byte) {
buffer = append(buffer, val)
}
您现在可以从每个 case
调用 f
。
案例可以合并,但 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)
}
}
我有一个函数可以接受多种不同的参数类型。我想使用类型开关并尽可能减少代码重复。作为一个非常基本的示例,在这里我想将 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)) // 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
的问题。但我真的在打开类型!啊!
我是否坚持这里的代码重复,或者是否有更聪明的方法来做到这一点?请注意,复制到字节缓冲区 用于说明示例,我的函数可能在 case 块中做其他事情。
这是一种避免代码重复的方法,代价是……好吧,另一种代码重复:
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)
部分变得更大或更复杂,这可能会更好。
在其他情况下——也许是最真实的情况下——方法
f := func(val byte) {
buffer = append(buffer, val)
}
您现在可以从每个 case
调用 f
。
案例可以合并,但 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)
}
}