Go 使代码更通用

Go 使代码更通用,go,Go,我有一个程序,其中许多功能在不同的结构中是相似的,然而,我最终一次又一次地编写这些函数,特别是因为其中处理的变量具有不同的结构 我在这里编写了一个示例代码 主程序包 输入“fmt” func(a*匹配)添加(v匹配){ a、 跑步+=v.跑步 a、 点数+=v.点数 } 类型匹配结构{ 运行uint64 点uint64 } func(a*活动)添加(v活动){ a、 步行 a、 点动+=v.点动 } 类型活动结构{ 步行街64 点动uint64 } func GetDailyMatches()

我有一个程序,其中许多功能在不同的结构中是相似的,然而,我最终一次又一次地编写这些函数,特别是因为其中处理的变量具有不同的结构

我在这里编写了一个示例代码

主程序包
输入“fmt”
func(a*匹配)添加(v匹配){
a、 跑步+=v.跑步
a、 点数+=v.点数
}
类型匹配结构{
运行uint64
点uint64
}
func(a*活动)添加(v活动){
a、 步行
a、 点动+=v.点动
}
类型活动结构{
步行街64
点动uint64
}
func GetDailyMatches()映射[字符串]匹配{
var dailyMatches映射[字符串]匹配
Match1,Match2:=匹配{5,10},匹配{1,2}
dailyMatches=make(映射[字符串]匹配)
dailyMatches[“01”]=匹配1
dailyMatches[“02”]=匹配2
dailyMatches[“03”]=匹配1
dailyMatches[“04”]=匹配2
每日归还火柴
}
func GetDailyActivities()映射[字符串]活动{
var dailyActivities映射[字符串]活动
Activity1,Activity2:=活动{5,10},活动{1,2}
dailyActivities=make(映射[字符串]活动)
每日活动[“01”]=活动1
每日活动[“02”]=活动2
每日活动[“03”]=活动1
每日活动[“04”]=活动2
返回日常活动
}
func main(){
格式打印(计算摘要(“01”、“03”))
格式打印(计算活动摘要(“02”、“04”))
格式打印(计算摘要(“01”、“03”))
格式打印(计算活动摘要(“02”、“04”))
}
func CalculateMatchSummary(开始、结束字符串)(完全匹配){
dailyMatches:=GetDailyMatches()
对于天,值:=范围dailyMatches{
如果一天开始{
持续
}否则,如果一天>结束{
持续
}否则{
总计。添加(值)
}
}
返回
}
func CalculateActivitySummary(开始、结束字符串)(总活动){
dailyActivities:=GetDailyActivities()
对于日,值:=范围日活动{
如果一天开始{
持续
}否则,如果一天>结束{
持续
}否则{
总计。添加(值)
}
}
返回
}
如果您注意到,
Match
Activity
具有相同的功能和结构,只是它们内部的结构不同

有没有一种简单的方法可以让Golang本身的代码更通用(Go中没有的Go泛型)?

Go有一个漂亮的包“reflect”。严格来说,您可以执行泛型,但您可以为相同的行为获得统一的代码。 我稍微改变了你的操场:

主要部分:

func AddTwo(a, b interface{}) interface{} {
    va := reflect.ValueOf(a)
    vb := reflect.ValueOf(b)
    res := reflect.New(reflect.TypeOf(a)).Elem()
    if va.Kind() != reflect.Struct && vb.Kind() != reflect.Struct {
        return nil
    }

    na, nb := va.NumField(), vb.NumField()
    if na != nb {
        return nil
    }

    for i := 0; i < na; i++ {
        // additional verification needed here
        fa := va.Field(i).Uint()
        fb := vb.Field(i).Uint()
        fr := fa + fb
        res.Field(i).SetUint(fr)
    }

    return res.Interface()
}
func AddTwo(a,b接口{})接口{}{
va:=反射值(a)
vb:=反射值(b)
res:=reflect.New(reflect.TypeOf(a)).Elem()
如果va.Kind()!=reflect.Struct&&vb.Kind()!=reflect.Struct{
归零
}
na,nb:=va.NumField(),vb.NumField()
如果na!=nb{
归零
}
对于i:=0;i
我使用reflect检查给定结构的字段。如果两者都是uint64,我可以反射地添加它们。如果您的结构包含许多uint64,它可以将它们全部添加

请注意,调用此函数后,必须将结果接口转换为给定的结构类型。这就是为什么这不是严格的泛型,因为返回的类型是接口,而不是匹配或活动


编辑:甚至不需要返回新结构。您可以通过调用.SetUint()方法简单地更新“a”结构的字段。

您可以将if块放在一个方法中。@ergonaut
total
类型不同。它还能用吗?我想不行@因此,是的,我意识到
泛型
不受支持。但在那之前,任何解决办法都没有。任何其他聪明的方法来处理这个问题。我认为golang没有添加泛型,因为还有其他方法可以做到这一点。这不是真的吗?这两个数据结构真的是耦合的吗?换句话说,改变一个人的基本功能是否应该改变另一个人?如果是,您可以让它们共享数据存储类型并使用组合。如果不是,go的设计鼓励自由使用轻量级接口。在这种情况下,您可能希望重新编写代码,以便两种数据类型都有一个公共接口,因此可以在同一方法中使用这两种数据类型。
func AddTwo(a, b interface{}) interface{} {
    va := reflect.ValueOf(a)
    vb := reflect.ValueOf(b)
    res := reflect.New(reflect.TypeOf(a)).Elem()
    if va.Kind() != reflect.Struct && vb.Kind() != reflect.Struct {
        return nil
    }

    na, nb := va.NumField(), vb.NumField()
    if na != nb {
        return nil
    }

    for i := 0; i < na; i++ {
        // additional verification needed here
        fa := va.Field(i).Uint()
        fb := vb.Field(i).Uint()
        fr := fa + fb
        res.Field(i).SetUint(fr)
    }

    return res.Interface()
}