【问题标题】:Solution for type assertion without generics with Golang?使用 Golang 解决没有泛型的类型断言?
【发布时间】:2018-05-02 10:37:12
【问题描述】:

我正在使用 gorm,它允许许多数据类型,例如 int, uint, int8, uint8 ....

然后我在模板中有一个这样的插件:

f["UNIX2STR"] = func(t interface{}, f string) string {
        switch t.(type) {
        case int:
            return time.Unix(int64(t.(int)), 0).Format(f)
        case uint:
            return time.Unix(int64(t.(uint)), 0).Format(f)
        case uint8:
            return time.Unix(int64(t.(uint8)), 0).Format(f)
        case *int:
            return time.Unix(int64(*t.(*int)), 0).Format(f)
        case *uint:
            return time.Unix(int64(*t.(*uint)), 0).Format(f)
        case *uint8:
            return time.Unix(int64(*t.(*uint8)), 0).Format(f)
        .....
        default:
            return ""
        }
        // return time.Unix(int64(t), 0).Format(f)
    }

它将所有整数类型转换为格式化字符串。 那我该怎么办?列出所有 gorm 支持的 int 类型并将其转换为 int64?

我已经搜索了很多天的解决方案,将 interface{} 转换为它的真实类型而不使用类型断言,但没有奏效。

【问题讨论】:

  • 你应该做什么?你想完成什么?
  • @Flimzy 我不想列出所有整数类型,只用一行来完成。喜欢return time.Unix(int64(t.(trueType)), 0).Format(f)。你没看到这里的麻烦吗?使用单个功能,我必须至少切换/案例 8 次才能完成一项简单的任务
  • @Flimzy I have searched many days for solution convert interface{} to its true type without using type assertion but didn't work.没看过这行?
  • 我看不出有什么问题,因为我看不出你想要完成什么。您专注于特定的解决方案,但尚未解释问题。您需要该 switch 语句的原因是什么?您要达到的目标是什么?
  • 多年来,我一直在使用 Go 通过 gorm 和类似的库访问数据库,并且从未觉得需要像您在问题中那样使用大型 switch 语句。几乎可以肯定有一种不同的方法可以解决您的问题,但是如果不知道您的目标,就很难说。换句话说,这看起来像是一个 XY 问题。

标签: generics go casting type-conversion type-assertion


【解决方案1】:

我没有用过gorm,但我认为这样的东西可以解决你的问题:

func formatUnix(t interface{}, f string) (string, error) {
    timestampStr := fmt.Sprint(t)
    timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
    if err != nil {
        return "", err
    }
    return time.Unix(timestamp, 0).Format(f), nil
}

它没有列出所有可能的类型,而是简单地将interface{} 转换为string,使用fmt.Sprint(),然后将string 转换为int64,使用strconv.ParseInt()

【讨论】:

    【解决方案2】:

    根据您的 cmets,您似乎关心将任何数字类型转换为字符串。这很容易通过fmt.Sprint 完成:

    stringValue := fmt.Sprint(i) // i is any type
    

    但这与 GORM 无关。

    另一方面,如果您的问题是 GORM 返回不可预测的类型,只需将您的 select 语句更改为始终返回一个字符串。例如,对于 MySQL,类似:

    SELECT CAST(someNumberColumn AS VARCHAR) AS stringColumn
    

    SELECT CAST(someNumberColumn AS INT) AS intColumn
    

    【讨论】:

    • Gorm同时支持指针类型和普通类型,日期时间字段可以是int*int,也就是说如果字段类型是指针,模板函数不能将其转换为字符串
    • 我不知道你在说什么模板函数,但是由于这个问题不涉及模板,所以可能需要单独的问题。
    • 这是问题的一部分。你看我在问题*int 中提到了指针类型,而Sprint(*i) 不适用于指针类型
    【解决方案3】:

    我不认为这是 go 或 gorm 的问题。我有点困惑你以许多不同的格式保存你的 unix 时间戳。顺便说一句,unix 时间戳是 32 位的,因此转换(并首先保存)任何 8 位整数是没有意义的。

    一种解决方案是对结构中的所有时间戳使用统一的数据类型 (int64)。之后,您的格式化函数可以接受int64 而不是interface{},而无需任何类型断言。

    【讨论】:

    • 我没有说这是gorm的问题。这是golang的问题,它不支持泛型。这就是为什么我们必须使用 switch/case 来键入断言所有类型的原因。使用int64 会导致内存。为什么我必须使用 int64 字段只有 0,1,2 个数字。这是设计模式,我们不能强迫其他开发人员在日期时间字段中只使用int64,而gorm 支持所有整数类型,这意味着任何人都可以对他们想要的任何字段使用任何类型,所以逻辑代码必须处理所有这一切。
    • 我不建议对所有数字字段使用 int64。只需将它用于时间戳。如果您关心内存,可以使用 int32,但对于 unix 时间戳,它应该至少为 32 位。将任何其他数字打印为格式化的日期字符串并没有真正意义,或者我错过了什么?因为某事是可能的,并不意味着你应该使用它。为什么要对相同类型的数据使用任意数量的数据类型?指定所有时间戳都以 32 位保存是一个明智的约定。然后你的函数可以接受int32 而不是interface{}
    • 数以百万计的代码逻辑。如上例:将 unixtimestamp(整数数据字段)转换为格式化字符串。从那时起,gorm 支持所有类型的整数。你会怎么做才能解决这个问题?就像我说的那样,明智的约定或常识是基于每个开发人员的观点,并不是每个人都有相同的编码风格,为了做好事,我们必须在代码逻辑中捕获所有案例。使用Sprint 实际上是一个不错的技巧,但不是解决泛型等问题的正确方法
    • 对不起,我只是想帮忙。我试图根据我的知识和理解回答你的问题。
    猜你喜欢
    • 2014-01-22
    • 2015-03-10
    • 2014-01-08
    • 2017-02-13
    • 2019-01-01
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    相关资源
    最近更新 更多