Go 如何对具有公共字段的空接口片段进行排序?

Go 如何对具有公共字段的空接口片段进行排序?,go,struct,interface,slice,Go,Struct,Interface,Slice,我有多个结构: 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-它们都被分成不同的切片:推进切片、推进切片、推进切片 2-我正在迭代每个片段,并将它们

我有多个结构:

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-它们都被分成不同的切片:推进切片、推进切片、推进切片

2-我正在迭代每个片段,并将它们未排序地插入到common
var commonSlice[]接口{}
片段中

3-然后我需要按CommonID将它们分类到该切片中,这就是我被卡住的地方

我正在努力做到:

sort.Slice(commonSlice, func(i, j int) bool { return commonSlice[i].CommonID > commonSlice[j].CommonID })
但我得到了错误

commonSlice[i].CommonID未定义(类型接口{}是没有方法的接口)

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

使用匿名结构和CommonID字段尝试了类似的操作,但没有成功

如果我直接转换被比较的实际结构的类型,我会假设它可以工作,但是硬编码该类型会破坏“commonSlice”的全部用途


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

因为
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)
这将输出(在上尝试):

而是创建一个接口,描述访问
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},
}
并将您的值存储在此界面的一部分中:

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()
})
这将输出相同的结果,请在上尝试

这是更干净,更快,可扩展

为了避免重复,您可以将公共字段和方法“外包”到结构中,并将其嵌入到所有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()
})
输出相同,请在上尝试


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

因为
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)
这将输出(在上尝试):

而是创建一个接口,描述访问
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},
}
并将您的值存储在此界面的一部分中:

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()
})
这将输出相同的结果,请在上尝试

这是更干净,更快,可扩展

为了避免重复,您可以将公共字段和方法“外包”到结构中,并将其嵌入到所有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()
})
输出相同,请在上尝试


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

。非常感谢。哦,很好。非常感谢。“我应该以不同的方式做事吗?”是的,不要使用空界面。是的,icza已经回答了这个问题,我不再使用自定义界面。无论如何,感谢您的巨大贡献。“我应该以不同的方式做事吗?”是的,不要使用空界面。是的,icza已经回答了,我不再使用自定义界面。无论如何,感谢您的巨大贡献。