2017-03-25 50 views
0

我的代碼一般是這樣的:轉:類型切換聲明中的這種多重情況條件有什麼問題?

func BulkInsert(docs interface{}) { 
    switch data := docs.(type) { 
     case map[string] *model.SnapshotByConv, map[string] *model.UserSnapshotsMap: 
      for ver, _ := range data { 
       // other logics ... 
      } 
     case map[int64] map[string] *model.Delta: 
      for ver, _ := range data { 
       // other logics ... 
      } 
    } 
} 

然後當編譯我得到了錯誤: cannot range over data (type interface {}),它是由第一range提高。

如果我刪除了多種在第一種情況下,這意味着把它作爲case map[string] *model.SnapshotByConv:

然後編譯錯誤消失了,這是wried,我需要繼續在這兩種類型完全相同的邏輯,所以爲什麼我不能把它們放在同一個case

請幫助,謝謝。

我認爲這裏的情況與已經在這裏得到的答案不一樣:golang multiple case in type switch,它試圖找到一種方法來識別類型,但這裏是我只是不想識別某些類型,只是運行它的一些邏輯,我找不到一個優雅的方式來做到這一點。

+1

可能重複[golang multiple case in type switch](http://stackoverflow.com/questions/40575033/golang-multiple-case-in-type-switch) –

+0

@timCooper是的,那麼我知道它不會像上面這樣工作,但我的情況下優雅的解決方案是什麼?我不需要確定這兩種類型,只需要對數據進行排序。試圖把代碼放在'default'中,仍然編譯錯誤。 – lnshi

+2

如果你想讓相同的代碼對不同的類型進行操作,那通常意味着你應該定義一個接口來封裝所需的功能,然後爲這些類型實現該接口。 –

回答

1

如果案例列舉多種類型(如您的例子),變量的類型將不會縮小,因此您的案例將是interface{}。這就是你鏈接到的問題所說的,並且沒有辦法繞過它。

要爲兩種不同類型的情況派發一些通用代碼,您需要在中間介紹一個接口。 (如果Go在某些時候引入泛型,但從外觀來看,這種情況可能會發生變化,即使這種情況發生也不會很快發生。)有幾種方法可以做到這一點,這裏有三種:

1 - Define類型爲您的地圖,並添加一個通用接口

不要直接使用地圖,而要定義類型爲地圖,併爲其添加功能。

type SnapshotMap map[string]*model.SnapshotByConv 
type UserSnapshotMap map[string]*model.UserSnapshotsMap 

func (sm *SnapshotMap) DoLogic() {/*...*/} 
func (usm *UserSnapshotMap) DoLogic() {/*...*/} 

type LogicDoer interface { 
    DoLogic() 
} 

// ... 

switch data := docs.(type) { 
case LogicDoer: 
    data.DoLogic() 
// ... 
} 

2 - 使地圖保持通用的接口

不要創建指針的地圖,但你的快照類型的共享接口映射。

type Snapshot interface { 
    LogicCommonToSnapshots() 
} 
var doc interface{} 

// Somewhere else, this happens rather than a creating map[string]*Something 
doc = make(map[string]Snapshot) 

// ... 

switch data := doc.(type) { 
case map[string]Snapshot: 
    for ver, _ := range data { 
     ver.LogicCommonToSnapshots() 
    } 
// ... 
} 

3 - 打出來的邏輯,並使其回叫到公共接口

拆分開關殼體分成兩個獨立的情況下,但打出來的邏輯轉換成可以在任快照類型操作的功能。這樣,你不必重複邏輯。

type Snapshot interface { 
    LogicCommonToSnapshots() 
} 

func ComplexLogic(s Snapshot) {/*...*/} 

switch data := doc.(type) { 
case map[string] *model.SnapshotByConv: 
    for ver, _ := range data 
     ComplexLogic(ver) 
    } 
case map[string] *model.UserSnapshotsMap: 
    for ver, _ := range data { 
     ComplexLogic(ver) 
    } 
// ... 
} 

請注意,在所有這三種情況下,都有一個接口將公共邏輯與邏輯所作用的對象分開。必須必然引入邏輯和對象之間的動態綁定,以通過接口或例如完成您所描述的內容。創建一個捕獲對象的匿名函數傳遞給邏輯。

+0

比你非常爲這個如此詳細的答案和真棒解釋。事實證明,最後我需要'json.Marshal'數據,所以即使對於這兩種類型,我仍然需要清楚地知道類型,底層結構定義。再次感謝你。 – lnshi

+0

你不需要有一個類型化的變量將其編組爲json。如果你看看'json.Marshal'的簽名(https://golang.org/pkg/encoding/json/#Marshal),你會發現它需要'interface {}'。 'json'包使用反射來找出結構定義。 –