【问题标题】:Go: cast any int value to int64 in type switchGo:在类型开关中将任何 int 值转换为 int64
【发布时间】:2013-06-20 18:10:35
【问题描述】:

我经常遇到这样的情况,我期望 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{} 类型。对于每个整数类型,我真的必须将其拆分为 cases 吗?

我知道我可以使用reflect.ValueOf(v).Int() 通过反射来做到这一点,但难道不应该有更好(不那么冗长)的方法吗?

更新:

提交了一个问题,Rob 建议在这种情况下只使用 reflect

【问题讨论】:

  • 你真正想解决什么问题?为什么原始类型未知?
  • 更好的方法是让你的函数接受 int64 并将转换留给调用者。
  • @iliacholy 如果考虑参数列表func(format string, args...interface{}) 是不可能的,本例就是这种情况。
  • 对不起,是的,罗伯。把 rsc@ 和 r@ 搞混了。

标签: casting go


【解决方案1】:

如果没有更多上下文,很难给你一个意见,但看起来你正试图让你的实现过于通用,这对于主要使用更多动态语言或具有通用支持的人来说很常见。

学习围棋过程的一部分是学习接受它的类型系统,这取决于你来自哪里,这可能具有挑战性。

通常,在 Go 中,您希望支持一种可以保存您需要处理的所有可能值的类型。在您的情况下,它可能是 int64。

例如,看看 math 包。它仅适用于 int64,并希望任何使用它的人都能正确地进行类型转换,而不是尝试转换所有内容。

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

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

干杯

【讨论】:

  • 我只是认为该语言可以为这种情况提供intish 等类型。这只是不必要的冗长。
  • 是的,当我开始使用 Go 时,我也有同样的感觉。最后,这是一个权衡。它牺牲了一点魔力,为您提供更多的正交性以及对执行和内存管理的控制,这对于大型应用程序来说非常重要。
【解决方案2】:

使用reflect 包。请注意,这可能比展开开关要慢很多。

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
}

【讨论】:

  • 我知道我可以使用反射。问题是如何在没有反思的情况下做到这一点。
  • 恐怕 peterSO 描述的方式是你应该如何使用 Go 方式。
【解决方案3】:

你想解决什么问题?您描述的完整解决方案如下所示:

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

【讨论】:

  • 我要解决的问题是您刚刚列出的冗余开关盒。
  • switch case 不是多余的,类型是不同的。
  • 我知道它们是不同的。冗余的意思是,我必须列出每个不同的类型,当它们显然都适合 int64/uint64 值时。
  • 是的,您必须列出所有不同的类型,因为在每个 case 语句中,类型是该案例的不同类型。在Num64 中的每个case 子句之后插入fmt.Printf("%T, %#v\n", n, n) 语句以查看是否正确。每种情况都进行不同的数值转换,生成不同的转换代码。
  • @peterSO 我也试图按照 OP 的要求去做。这个想法是为什么编译器不理解int64(n) 可以与给定的所有类型一起使用?希望可能是保持代码干燥。
【解决方案4】:

你可以做这样的事情...转换成一个字符串然后解析这个字符串。效率不高但紧凑。我把这个例子放在了 Go 操场上: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

【讨论】:

  • 这是一个糟糕的建议。为什么有人会选择使用reflect
  • 这仍然是一个解决方案
  • 它只适用于 int 类型;它不适用于 int 类型和 uint 类型。
  • 是的,我同意第二个想法,这不是一个好的解决方案。它是一种有时在 JavaScript 中使用的技术(即 tostring 然后 parseInt),并且根本不能很好地转换为 Go。
猜你喜欢
  • 2016-12-09
  • 2022-01-24
  • 1970-01-01
  • 2011-05-02
  • 2014-02-22
  • 2012-09-20
  • 1970-01-01
  • 2018-07-04
  • 2011-12-22
相关资源
最近更新 更多