假设我有一堆结构(大约10个)。
function addHand() {
var htmlString;
for(i = 0; i < hands[currentPlayersTurn].handArray.length; i++) {
htmlString += "/ "+ hands[currentPlayersTurn].handArray[i].name +" ";
}
$("#playerHand").html(htmlString);
}
如果我在任何给定时间(type A struct {
ID int64
... other A-specific fields
}
type B struct {
ID int64
... other B-specific fields
}
type C struct {
ID int64
... other C-specific fields
}
,[]A
或[]B
)都有这些结构的数组,我怎样才能编写一个从中提取ID的函数结构数组没有写3(或在我的情况下,10)单独的函数,如下:
[]C
答案 0 :(得分:2)
据我所知,没有简单的方法。
您可能很想使用embedding,但我不确定是否有任何方法可以更轻松地完成此特定任务。嵌入感觉就像子类化,但它并没有给你多态性的力量。
Go中的多态性仅限于方法和接口,而不是字段,因此您无法在多个类中按名称访问给定字段。
您可以使用reflection按名称(或标记)查找和访问您感兴趣的字段,但会对此产生性能损失,这将使您的代码变得复杂且难以遵循。反射并不是真正意图替代多态或泛型。
我认为你最好的解决方案是使用Go为你提供的多态性,并创建一个接口:
type IDable interface {
GetId() int64
}
为每个类创建一个GetId方法。 Full example
答案 1 :(得分:2)
如果定义一个通用接口来访问切片的i
th 元素的ID,则可以使它更简单一些:
type HasIDs interface {
GetID(i int) int64
}
您为这些提供了实施:
func (x AList) GetID(i int) int64 { return x[i].ID }
func (x BList) GetID(i int) int64 { return x[i].ID }
func (x CList) GetID(i int) int64 { return x[i].ID }
然后一个GetID()
功能就足够了:
func GetIDs(s HasIDs) (ids []int64) {
ids = make([]int64, reflect.ValueOf(s).Len())
for i := range ids {
ids[i] = s.GetID(i)
}
return
}
注意:切片的长度可以是GetIDs()
的参数,也可以是HasIDs
接口的一部分。两者都比微小的反射调用更复杂,以获得切片的长度,所以请耐心等待。
使用它:
as := AList{A{1}, A{2}}
fmt.Println(GetIDs(as))
bs := BList{B{3}, B{4}}
fmt.Println(GetIDs(bs))
cs := []C{C{5}, C{6}}
fmt.Println(GetIDs(CList(cs)))
输出(在Go Playground上尝试):
[1 2]
[3 4]
[5 6]
请注意,我们可以使用AList
,BList
等类型的切片,我们不需要使用interface{}
或[]SomeIface
。另请注意,我们也可以使用例如一个[]C
,当它传递给GetIDs()
时,我们使用了一个简单类型conversion。
这很简单。如果你想要消除切片的GetID()
方法,那么你真的需要深入研究反射(reflect
包),它会更慢。上面提出的解决方案与“硬编码”版本大致相同。
如果你想让它完全“通用”,你可以使用反射来做,然后你就不需要任何额外的方法。
不检查错误,这是解决方案:
func GetIDs(s interface{}) (ids []int64) {
v := reflect.ValueOf(s)
ids = make([]int64, v.Len())
for i := range ids {
ids[i] = v.Index(i).FieldByName("ID").Int()
}
return
}
测试和输出(几乎)相同。请注意,由于此处GetIDs()
的参数类型为interface{}
,因此您无需转换为CList
即可传递[]C
类型的值。在Go Playground上尝试。
通过将其名称指定为string
来获取字段非常脆弱(例如,考虑重命名/重构)。如果我们将ID
字段和访问器方法“外包”到我们将嵌入的单独的struct
,我们可以提高可维护性,安全性和某种反射性能,并且我们通过接口:
type IDWrapper struct {
ID int64
}
func (i IDWrapper) GetID() int64 { return i.ID }
type HasID interface {
GetID() int64
}
所有类型都嵌入IDWrapper
:
type A struct {
IDWrapper
}
type B struct {
IDWrapper
}
type C struct {
IDWrapper
}
通过嵌入,所有嵌入类型(A
,B
,C
)都会提升GetID()
方法,因此它们都会自动实现HasID
。我们可以在GetIDs()
函数中利用这一点:
func GetIDs(s interface{}) (ids []int64) {
v := reflect.ValueOf(s)
ids = make([]int64, v.Len())
for i := range ids {
ids[i] = v.Index(i).Interface().(HasID).GetID()
}
return
}
测试它:
as := AList{A{IDWrapper{1}}, A{IDWrapper{2}}}
fmt.Println(GetIDs(as))
bs := BList{B{IDWrapper{3}}, B{IDWrapper{4}}}
fmt.Println(GetIDs(bs))
cs := []C{C{IDWrapper{5}}, C{IDWrapper{6}}}
fmt.Println(GetIDs(cs))
输出是一样的。在Go Playground上试一试。请注意,在这种情况下,唯一的方法是IDWrapper.GetID()
,不需要定义其他方法。
答案 2 :(得分:0)
通用方法需要使用接口和反射。