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:

然后编译错误消失了,写出来了,我需要对这两种类型进行完全相同的逻辑,那为什么我不能把它们放在同一个case

请帮忙,谢谢。

我认为这里的情况与这里已经有答案的情况不同:golang multiple case in type switch,它试图找到一种识别类型的方法,但我只是不想识别某些类型,只是简单地在上面运行一些逻辑,我找不到一种优雅的方法来做到这一点。

4

1 回答 1

2

If the case enumerates multiple types (like it does in your example), the variable's type won't be narrowed, so in your case it will be interface{}. This is what the question you link to says, and there is no way around it.

To dispatch some generic code for two different type cases, you will need to introduce an interface somewhere inbetween. (This might change if Go at some point introduces generics, but from the looks of it, that won't happen soon if ever.) There are a few ways you could do this, here are three:

1 - Define types for your maps, and add a common interface

Don't use the maps directly, but define types that are maps, and add functions to them.

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 - Make the maps hold a common interface

Don't create maps of pointers, but maps of interfaces that your snapshot types share.

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 - Break out the logic and make it call back onto a common interface

Split the switch case into two separate cases, but break out the logic into a function that can operate on either snapshot type. That way, you won't have to duplicate the logic.

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)
    }
// ...
}

Notice that in all three cases, there is an interface that separates the common logic from the object that logic acts on. You must necessarily introduce a dynamic binding between the logic and the object to accomplish what you describe, either through interfaces or by e.g. creating an anonymous function that captures the object gets passed to the logic.

于 2017-03-25T19:24:34.883 回答