2017-03-25 80 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'包使用反射来找出结构定义。 –