Go:在类型开关中将任何int值转换为int64

18

我经常遇到这样的情况,我需要一个int(任何类型的int/int8/16/32/64),并使用类型切换进行检查。

switch t := v.(type) {
  case int, int8, int16, int32, int64:
    // cast to int64
  case uint, uint8, uint16, uint32, uint64:
    // cast to uint64
}

现在我不能使用直接转换,因为在这种情况下,t将是interface{}类型。我真的需要将其拆分为每个整数类型的case吗?
我知道可以通过使用reflect.ValueOf(v).Int()进行反射来完成,但是否应该有更好(更简洁)的方法来完成此操作?
更新:提交了一个问题,并建议在这种情况下只使用reflect

1
你实际上要解决的问题是什么?原始类型为什么未知? - fuz
5
更好的方法是让您的函数接受一个 int64,将类型转换留给调用者处理。 - Ilia Choly
@iliacholy 不可能,如果你考虑到参数列表 func(format string, args...interface{}),这就是这个例子的情况。 - user187676
Rob,不是Russ。[问题5743:规范:类型开关范围状态:WontFix](http://code.google.com/p/go/issues/detail?id=5743#c3) - peterSO
啊,抱歉,是的,罗布。我把rsc@和r@搞混了。 - user187676
4个回答

24

没有更多的上下文很难给出意见,但看起来你试图使你的实现过于通用,这是那些大多数使用动态语言或一般性支持的人所共有的问题。

学习 Go 的过程中,接受其类型系统是其中的一部分,取决于你从哪里来,可能会面临挑战。

通常,在 Go 中,你想要支持一个可以容纳你需要处理的所有可能值的类型。在你的情况下,可能是 int64。

例如,看一下 math 包。它只能处理 int64 类型,并期望任何使用者正确地进行类型转换,而不是试图对所有东西进行转换。

另一种选择是使用接口来实现类型无关性,就像 sort 包一样。基本上,任何特定于类型的方法都将在包外实现,并期望定义某些方法。

学习并接受这些属性需要一段时间,但总的来说,在最终,它被证明在可维护性和健壮性方面都非常好。接口确保您具有正交性,强类型确保您控制类型转换,这最终可能导致错误以及内存中的不必要副本。

干杯


2
我认为编程语言可以为这种情况提供 intish 等类型。这只是不必要的冗长。 - user187676
1
是的,我开始学习Go的时候也有同样的感觉。最终,这是一种权衡。它牺牲了一些魔力,以便为您提供更多的正交性和对执行和内存管理的控制,这在大型应用程序中非常重要。 - Rodrigo Kochenburger

14

你试图解决什么问题?你所描述的完整解决方案如下:

func Num64(n interface{}) interface{} {
    switch n := n.(type) {
    case int:
        return int64(n)
    case int8:
        return int64(n)
    case int16:
        return int64(n)
    case int32:
        return int64(n)
    case int64:
        return int64(n)
    case uint:
        return uint64(n)
    case uintptr:
        return uint64(n)
    case uint8:
        return uint64(n)
    case uint16:
        return uint64(n)
    case uint32:
        return uint64(n)
    case uint64:
        return uint64(n)
    }
    return nil
}

func DoNum64Things(x interface{}) {
    switch Num64(x).(type) {
    case int64:
        // do int things
    case uint64:
        // do uint things
    default:
        // do other things
    }
}

6
我尝试解决的问题是您刚刚列出的冗余开关案例。 - user187676
4
分支语句并非冗余,因为类型是不同的。 - peterSO
1
我知道它们是不同的。我所说的冗余是指,当所有类型都适合于int64/uint64值时,我必须列出每个不同的类型。 - user187676
是的,您必须列出所有不同的类型,因为在每个case语句内部,该类型是该case的不同类型。在“Num64”中的每个case子句之后插入fmt.Printf(“%T,#v \ n”,n,n)语句,以查看这是真的。每种情况都有不同的数字转换,生成不同的转换代码。 - peterSO
@peterSO 我也在尝试按照 OP 的要求进行操作。想法是为什么编译器不能理解 int64(n) 适用于所有给定的类型?希望能保持代码DRY。 - Mike Graf

8
使用反射(reflect)包。请注意,这比展开switch语句慢得多。
switch t := v.(type) {
case int, int8, int16, int32, int64:
    a := reflect.ValueOf(t).Int() // a has type int64
case uint, uint8, uint16, uint32, uint64:
    a := reflect.ValueOf(t).Uint() // a has type uint64
}

我知道可以使用反射。问题是如何在没有反射的情况下实现它。 - user187676
1
我担心 PeterSO 描述的方式是使用 Go 的正确方法。 - fuz

-1
你可以这样做...将其转换为字符串,然后解析该字符串。虽然不是很高效,但很紧凑。我已经在Go playground中放置了这个例子:http://play.golang.org/p/0MCbDfUSHO
package main

import "fmt"
import "strconv"

func Num64(n interface{}) int64 {
    s := fmt.Sprintf("%d", n)
    i,err := strconv.ParseInt(s,10,64)
    if (err != nil) {
        return 0
    } else {
        return i
    }
}

func main() {
    fmt.Println(Num64(int8(100)))
    fmt.Println(Num64(int16(10000)))
    fmt.Println(Num64(int32(100000)))
    fmt.Println(Num64(int64(10000000000)))
    fmt.Println(Num64("hello"))
}

// Outputs:
// 100
// 10000
// 100000
// 10000000000
// 0

1
这是一个糟糕的建议。为什么有人会选择这样做而不是使用“reflect”呢? - user187676
2
这仍然是一个解决方案。 - TheBrain
1
它只适用于 int 类型;它不适用于 int 类型和 uint 类型。 - peterSO
是的,我改变主意了,这不是一个好的解决方案。这是 JavaScript 中有时使用的一种技术(即 tostring 然后 parseInt),在 Go 中无法很好地转换。 - dodgy_coder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接