【问题标题】:How to Sort a slice of empty Interface that have a field in common?如何对具有共同字段的空接口切片进行排序?
【发布时间】:2020-11-29 22:37:32
【问题描述】:

我有多个结构:

type FooStruct struct {
  ID int
  Field1 int
  CommonID int
}

type BarStruct struct {
  ID int
  Field2 int
  CommonID int
}

type FighterStruct struct {
  ID int
  Field3 int
  CommonID int
}

1- 它们都保存在不同的切片中: sliceOfFooStruct 、 sliceOfBarStruct 、 sliceofFighterStruct

2- 我正在遍历每个切片并将它们未排序地插入到一个 commun var commonSlice []interface{} 切片中

3- 然后我需要按 CommonID​​ 将它们排序到该切片中,这就是我卡住的地方。

我正在努力:

sort.Slice(commonSlice, func(i, j int) bool { return commonSlice[i].CommonID > commonSlice[j].CommonID })

但我得到了错误

commonSlice[i].CommonID undefined (type interface {} is interface with no methods)

我也尝试过使用 commonSlice[i].CommonID​​.(int) 强制转换类型,但它也不起作用。

也尝试了类似this 的匿名结构和 CommonID​​ 字段,但没有成功。

如果我直接转换被比较的实际结构的类型,我认为它可以工作,但硬编码类型会破坏“commonSlice”的全部目的。

这是怎么做到的?我应该做不同的事情吗?

【问题讨论】:

  • “我应该做不同的事情吗?”是的,不要使用空接口。
  • 是的,icza 已经回答了,我不再使用自定义界面。无论如何,感谢您的巨大贡献。

标签: go struct interface slice


【解决方案1】:

由于commonSlice 的元素类型是interface{},因此您无法访问值的任何字段,因为这允许存储任何值,甚至是非结构的值。

一种不鼓励的方法是使用反射来访问CommonID 字段,但这既丑陋又缓慢。作为参考,它的外观如下:

all := []interface{}{
    FooStruct{11, 22, 31},
    BarStruct{21, 32, 33},
    FighterStruct{21, 32, 32},
}

sort.Slice(all, func(i, j int) bool {
    commonID1 := reflect.ValueOf(all[i]).FieldByName("CommonID").Int()
    commonID2 := reflect.ValueOf(all[j]).FieldByName("CommonID").Int()
    return commonID1 > commonID2
})

fmt.Println(all)

这个输出(在Go Playground上试试):

[{21 32 33} {21 32 32} {11 22 31}]

而是创建一个接口来描述访问CommonID 字段的方法:

type HasCommonID interface {
    GetCommonID() int
}

让你的struts实现这个接口:

func (f FooStruct) GetCommonID() int     { return f.CommonID }
func (b BarStruct) GetCommonID() int     { return b.CommonID }
func (f FighterStruct) GetCommonID() int { return f.CommonID }

并将您的值存储在此接口的一部分中:

all := []HasCommonID{
    FooStruct{11, 22, 31},
    BarStruct{21, 32, 33},
    FighterStruct{21, 32, 32},
}

然后您可以使用GetCommonID() 方法在less() 函数中访问它:

sort.Slice(all, func(i, j int) bool {
    return all[i].GetCommonID() > all[j].GetCommonID()
})

这将输出相同,请在Go Playground 上尝试。

这更清洁、更快、可扩展。

为避免重复,您可以将公共字段和方法“外包”到一个结构中,并将其嵌入到您的所有 struts 中:

type Common struct {
    CommonID int
}

func (c Common) GetCommonID() int { return c.CommonID }

type FooStruct struct {
    ID     int
    Field1 int
    Common
}

type BarStruct struct {
    ID     int
    Field2 int
    Common
}

type FighterStruct struct {
    ID     int
    Field3 int
    Common
}

注意:GetCommonID()只定义一次,Common,其他类型不需要添加。然后使用它:

all := []HasCommonID{
    FooStruct{11, 22, Common{31}},
    BarStruct{21, 32, Common{33}},
    FighterStruct{21, 32, Common{32}},
}

sort.Slice(all, func(i, j int) bool {
    return all[i].GetCommonID() > all[j].GetCommonID()
})

输出是一样的,在Go Playground上试试。

这还允许您在一个地方 (Common) 扩展公共字段和方法列表,而无需进一步重复。

【讨论】:

  • 哦,太好了。非常感谢。
猜你喜欢
  • 2017-08-29
  • 2018-08-26
  • 1970-01-01
  • 1970-01-01
  • 2018-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多