我正在编写允许从数据库访问数据的代码。但是,我发现自己为相似的类型和字段重复相同的代码。我怎样才能为它编写通用函数?
e.g。我想要实现的目标......
type Person{FirstName string}
type Company{Industry string}
getItems(typ string, field string, val string) ([]interface{}) {
...
}
var persons []Person
persons = getItems("Person", "FirstName", "John")
var companies []Company
cs = getItems("Company", "Industry", "Software")
答案 0 :(得分:2)
所以你肯定在回归一片零接口类型的想法上走在正确的轨道上。但是,当您尝试访问特定成员或调用特定方法时,您将遇到问题,因为您不会知道您正在寻找什么类型。这是类型断言将非常方便的地方。为了扩展你的代码:
getPerson(typ string, field string, val string) []Person {
slice := getItems(typ, field, val)
output := make([]Person, 0)
i := 0
for _, item := range slice {
// Type assertion!
thing, ok := item.(Person)
if ok {
output = append(output, thing)
i++
}
}
return output
}
那么它的作用是执行一般搜索,然后只清除那些类型正确的项目。具体来说,类型断言:
thing, ok := item.(Person)
检查变量item
是否为Person
类型,如果是,则返回值和true,否则返回nil和false(因此检查ok告诉我们断言是否成功)。
实际上,如果需要,您可以更进一步,并根据另一个布尔函数定义getItems()
函数。基本上这个想法是让getItems()
运行函数将它传递给数据库中的每个元素,并且如果在元素上运行函数返回true,则只将该元素添加到结果中:
getItem(critera func(interface{})bool) []interface{} {
output := make([]interface{}, 0)
foreach _, item := range database {
if criteria(item) {
output = append(output, item)
}
}
}
(老实说,如果是我,我会做两者的混合,接受一个标准函数,但也接受字段和值字符串)
答案 1 :(得分:2)
joshlf13有一个很好的答案。虽然为了保持一些额外的类型安全性,我会稍微扩展它。而不是critera函数我会使用收集器函数。
// typed output array no interfaces
output := []string{}
// collector that populates our output array as needed
func collect(i interface{}) {
// The only non typesafe part of the program is limited to this function
if val, ok := i.(string); ok {
output = append(output, val)
}
}
// getItem uses the collector
func getItem(collect func(interface{})) {
foreach _, item := range database {
collect(item)
}
}
getItem(collect) // perform our get and populate the output array from above.
这样做的好处是不需要在调用getItems之后遍历你的interface {}切片并进行另一次转换。