获取任何切片的快速功能



我想表达一个可以接受任何切片的函数。 我以为我可以这样做:

func myFunc(list []interface{}) {
for _, i := range list {
...
some_other_fun(i)
...
}
}

其中some_other_fun(..)本身采用interface{}类型。 但是,这不起作用,因为您无法将[]DEFINITE_TYPE传递为[]interface{}。 请参阅:https://golang.org/doc/faq#convert_slice_of_interface 其中指出 []接口{} 的表示是不同的。 这个答案总结了原因,但关于指向接口而不是接口切片的指针,但原因是一样的:为什么我不能将 *Struct 分配给 *接口?。

上面 golang.org 链接中提供的建议建议从DEFINITE_TYPE片重新构建新的接口片。 但是,在我想调用此函数的代码中,这样做是不切实际的(此函数本身仅缩写 9 行代码,但这 9 行在我们的代码中出现得非常频繁)。

在我想调用函数的每种情况下,我都会传递一个[]*DEFINITE_TYPE我最初认为它更容易抽象,直到我再次发现为什么我不能将 *Struct 分配给 *接口?(上面也有链接)。

此外,每次我想调用函数时,它都是用不同的DEFINITE_TYPE所以为 n 种类型实现 n 个示例不会为我节省任何代码行或使我的代码更清晰(恰恰相反!

令人沮丧的是,我无法做到这一点,因为 9 行在我们的代码中是惯用的,错误键入很容易引入错误。 我真的很想念泛型。 真的没有办法做到这一点吗?!!

在您提供的情况下,您必须将切片创建为interface的切片,例如s := []interface{}{}.此时,您可以将任何您想要的类型放入切片中(甚至是混合类型)。但是,您将不得不执行各种类型断言,并且一切都变得非常讨厌。

解编组者常用的另一种技术是这样的定义:

func myFunc(list interface{})

因为切片适合interface,所以您确实可以将常规切片传递到此切片中。您仍然需要在myFunc中执行一些验证和类型断言,但您将对整个列表类型执行单个断言,而不必担心可能包含混合类型的列表。

无论哪种方式,由于是静态类型语言,您最终必须知道通过断言传入的类型。事情就是这样。在您的情况下,我可能会使用上面的 func 签名,然后使用类型开关来处理不同的情况。请参阅此文档 https://newfivefour.com/golang-interface-type-assertions-switch.html

所以,像这样:

func myFunc(list interface{}) {
switch v := list.(type) {
case []string:
// do string thing
case []int32, []int64:
// do int thing
case []SomeCustomType:
// do SomeCustomType thing
default:
fmt.Println("unknown")
}
}

不,没有简单的方法来处理它。许多人错过了 Go 中的泛型。

也许您可以从sort.Sort函数中获得灵感,sort.Interface找到一个不需要复制切片的合理解决方案。

可能最好的办法是定义一个接口来封装myFunc需要对切片执行的操作(即,在您的示例中,获取第 n 个元素)。然后,函数的参数是该接口类型,并为要传递给函数的每种类型定义接口方法。

您也可以使用reflect包来做到这一点,但这可能不是一个好主意,因为如果您传递切片(或数组或字符串)以外的内容,它会感到恐慌。

func myFunc(list interface{}) {
listVal := reflect.ValueOf(list)
for i := 0; i < listVal.Len(); i++ {
//...
some_other_fun(listVal.Index(i).Interface())
//...
}
}

请参阅 https://play.golang.org/p/TyzT3lBEjB。

现在使用 Go 1.18+,您可以使用泛型功能来做到这一点:

func myFunc[T any](list []T) {
for _, item := range list {
doSomething(item)
}
}

相关内容

最新更新