這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
初步想法,我希望有一個大概的設計,Linq是什麼,這部分先不考究。
type User struct{ Id int Name string Birthday time.Time} From(userArr).Where(func(c interface{}) bool{ return c.Birthday > FormatBirthday("1989-01-17") }).Select(func(c interface{}) interface{} { return c.Name })
假設From,Where,Select返回都是Queryable 一個struct;為了實作類別似泛型的效果,Queryable 裡面的值是interface{};
type Queryable struct { values []interface{}}
From方法 構建這個Queryable
func From(input []interface{}) Queryable{ return Queryable{ values: input, }}
Where方法,傳遞一個用於比較的方法,而Golang沒有lambda(例如 x=>x.A > 100),就用匿名函數代替,使得Where方法跟具體類型無關,此匿名方法返回bool;跟From返回的Queryable組成一條方法鏈。
func (in Queryable) Where(condition func(interface{})(bool)) (r Queryable){ for _,i := range in.values{ ok := condition(i) if ok { r.values = append(r.values,i) } } return r}
Select方法,跟Where類似,但傳回值不是bool,而是Queryable。
func (in Queryable) Select(sel func(interface{}) interface{}) (r Queryable){ for _,i := range in.values{ r.values = append(r.values,sel(i)) } return r}
如果需要組合一個新的資料群組合,需要匿名型別。
.Select(func(s interface{}) interface{} { return struct{ Id int Name string}{s.(User).Id, s.(User).Name} })
這樣就大致實現了 from p in list where p.A > 100 select p.Id的模式了,還少了一個orderby。
撇開Linq不談,Golang對由特定使用者類型組成的數組的某個(暫時僅僅1個欄位)欄位的排序,是需要為此使用者類型實現Len,Less,Swap方法的,結合interface{},就很容易想到以下結構
type SortableQuery struct{ values []interface{}}func (list SortableQuery) Len() int{ return len(list.values)}func (list SortableQuery) Swap(i,j int){ list.values[i], list.values[j] = list.values[j], list.values[i]}//當寫到Less方法的時候要考慮OrderBy方法的設計func (list SortableQuery) Less(i,j int) bool
想想我們要實現OrderBy方法是讓數組按內在元素的某個欄位排序,使用的時候大概是
OrderBy(func(o interface) SortableQuery) SortableQuery{ return o.(User).Id}
也就是說OrderBy的方法要傳入一個方法:f。
f(values[i]) 獲得上面代碼的o.(User).Id,但這個傳回值是不確定的,可能是string,暫時先實現int和string兩種情況
func (in Queryable) OrderByDesc2(f func(interface{}) interface{}) (r SortableQuery) { less := func(i,j int) bool{ switch reflect.ValueOf(f(in.values[i])).Kind(){ case reflect.Int: o1 := f(in.values[i]).(int) o2 := f(in.values[j]).(int) return o1 > o2 case reflect.String: o1 := f(in.values[i]).(string) o2 := f(in.values[j]).(string) return strings.Compare(o1,o2) == 1 default: return false } return false; } r.less = less r.values = in.values sort.Sort(r) return r}
另外,如果不想把類型的變化封裝到OrderByDesc函數,可以sort.less方法作為使用者輸入,在orderby方法裡面實現;暫時認為這樣靈活度是更高一點。
現在留下一些問題,例如延遲計算,待續。。。
最後,參考一下開源的實現
GitHub - ahmetalpbalkan/go-linq: .NET LINQ-like query methods for Go