隨著在 Go 1.18 中添加泛型,現在是否可以提出與 C# 的LINQ to Objects等效的功能?
或者,與 C# 泛型相比,Go 的泛型在原則上是否缺少某些東西,這會使這變得困難或不可能?
例如,最初的101 個 LINQ 樣本(“LowNumbers”)中的第一個現在可以在 Go 中使用泛型實作,大致如下:
package main
import (
"fmt"
)
type collection[T comparable] []T
func (input collection[T]) where(pred func(T) bool) collection[T] {
result := collection[T]{}
for _, j := range input {
if pred(j) {
result = append(result, j)
}
}
return result
}
func main() {
numbers := collection[int]{5, 4, 1, 3, 9, 8, 6, 7, 2, 0}
lowNums := numbers.where(func(i int) bool { return i < 5 })
fmt.Println("Numbers < 5:")
fmt.Println(lowNums)
}
uj5u.com熱心網友回復:
(免責宣告:我不是 C# 專家)
Go 的引數多型性與 C# 或 Java 中泛型的實作之間的一個顯著區別是 Go(仍然)沒有用于型別引數的協變/逆變換的語法。
例如,在 C# 中,您可以擁有實作IComparer<T>和傳遞派生容器類的代碼;或者在 Java 中是流 API 中的典型Predicate<? super T>。在 Go 中,型別必須完全匹配,并且用不同的型別引數實體化一個泛型型別會產生不同的命名型別,它們不能相互分配。另請參閱:為什么 Go 不允許將一個泛型分配給另一個?
Go也不是OO,所以沒有繼承的概念。您可能有實作介面的型別,甚至是引數化介面。一個人為的例子:
type Equaler[T any] interface {
Equals(T) bool
}
type Vector []int32
func (v Vector) Equals(other Vector) bool {
// some logic
}
因此,使用此代碼,Vector實作了一個特定實體,Equaler即Equaler[Vector]. 為了清楚起見,下面的 var 宣告編譯:
var _ Equaler[Vector] = Vector{}
因此,有了這個,您可以撰寫通用的函式T并用于T實體化Equaler,并且您將能夠傳遞任何實作該特定實體的內容Equaler:
func Remove[E Equaler[T], T any](es []E, v T) []E {
for i, e := range es {
if e.Equals(v) {
return append(es[:i], es[i 1:]...)
}
}
return es
}
您可以使用 any 呼叫此函式T,因此使用T具有Equals(T)方法的 any :
// some other random type that implements Equaler[T]
type MyString string
// implements Equaler[string]
func (s MyString) Equals(other string) bool {
return strings.Split(string(s), "-")[0] == other
}
func main() {
vecs := []Vector{{1, 2}, {3, 4, 5}, {6, 7}, {8}}
fmt.Println(Remove(vecs, Vector{6, 7}))
// prints [[1 2] [3 4 5] [8]]
strs := []MyString{"foo-bar", "hello-world", "bar-baz"}
fmt.Println(Remove(strs, "hello"))
// prints [foo-bar bar-baz]
}
唯一的問題是只有定義的型別可以有方法,所以這種方法已經排除了所有復合的非命名型別。
然而,為了部分救援,Go 具有高階函式,因此使用該型別和非命名型別撰寫類似流的 API 并非不可能,例如:
func Where[C ~[]T, T any](collection C, predicate func(T) bool) (out C) {
for _, v := range collection {
if predicate(v) {
out = append(out, v)
}
}
return
}
func main() {
// vecs declared earlier
filtered := Where(vecs, func(v Vector) bool { return v[0] == 3})
fmt.Printf("%T %v", filtered, filtered)
// prints []main.Vector [[3 4 5]]
}
In particular here you use a named type parameter C ~[]T instead of just defining collection []T so that you can use it with both named and non-named types.
Code available in the playground: https://gotipplay.golang.org/p/mCM2TJ9qb3F
(Choosing the parametrized interfaces vs. higher-order functions probably depends on, among others, if you want to chain methods, but method chaining in Go isn't very common to begin with.)
Conclusion: whether that is enough to mimic LINQ- or Stream-like APIs, and/or enable large generic libraries, only practice will tell. The existing facilities are pretty powerful, and could become even more so in Go 1.19 after the language designers gain additional experience with real-world usage of generics.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/444033.html
上一篇:Go/Mongo:回傳單個物件
