结构类型作为映射键



我们有以下函数:

func (h *Handler) Handle(message interface{}) error {
//here there is a switch for different messages
switch m := message.(type) {
}
}

此签名已给出,无法更改。处理程序处理大约 20 种不同的消息类型。

现在,其中一些消息(大约 4 条(需要特殊的后处理。在不同的包装中。

因此,我想这样做:

func (h *Handler) Handle(message interface{}) error {
//here there is a switch for different messages
switch m := message.(type) {
}
//only post-process if original message processing succeeds
postProcessorPkg.Process(message)
}

现在,在Process函数中,我想快速查找消息类型是否确实是我们需要后处理的类型。我不想在这里再次做switch。有许多处理程序,在不同的包中,具有不同数量的消息类型,它应该是泛型的。

所以我正在考虑在后处理器中注册消息类型,然后进行查找:

func (p *Postprocessor) Register(msgtype interface{}) {
registeredTypes[msgtype] = msgtype
}

然后

func (p *Postprocessor) Process(msgtype interface{}) error {
if ok := registeredTypes[msgtype]; !ok {
return errors.New("Unsupported message type")
}
prop := GetProp(registeredTypes[msgtype])
doSmthWithProp(prop)
}

这现在都不起作用,因为据我所知,我只能"注册"消息的实例,而不能"注册"消息类型本身。因此,映射将仅匹配消息的特定实例,而不是其类型,这正是我需要的。

所以我想这需要重新设计。我可以完全放弃注册和地图查找,但是

  • 我无法将Handle函数更改为特定类型(签名需要保持message interface{}
  • 我想避免不得不使用reflect,只是因为我很难与一些同事一起捍卫这样的解决方案。

由于无法将类型设置为映射键,我最终决定实现以下解决方案,该解决方案基于@Chrono Kitsune的解决方案:

type Postprocess interface {
NeedsPostprocess() bool
}
type MsgWithPostProcess struct {}
func (p *MsgWithPostProcess) NeedsPostprocess() bool {
return true
}
type Msg1 struct {
MsgWithPostProcess
//other stuff
}
type Msg2 struct {
MsgWithPostProcess
//other stuff
}
type Msg3 struct {
//no postprocessing needed
}
func (p *Postprocessor) Process(msgtype interface{}) error {
if _, ok := msgtype.(Postprocess); ok {
//do postprocessing
}         
}

在我所做的简单测试中,只有Msg1Msg2会被后处理,而不是Msg3,这就是我想要的。

这个问题是我在谷歌上找到的第一个命中,但标题有点误导。因此,我将把这个留在这里,以补充一些思考的内容,并牢记问题的标题。

首先,地图的问题在于其键必须是可比较的值。这就是为什么例如,切片不能使用是映射键的原因。切片不可比较,因此不允许使用。出于同样的原因,您可以使用数组(固定大小的切片(,但不能使用切片。

其次,您reflect.TypeOf(...).String()有一种获取类型的规范字符串表示形式的方法。尽管除非您包含包路径,否则它不是明确的,如下所示。

package main
import (
"fmt"
s2 "go/scanner"
"reflect"
s1 "text/scanner"
)
type X struct{}
func main() {
fmt.Println(reflect.TypeOf(1).String())
fmt.Println(reflect.TypeOf(X{}).String())
fmt.Println(reflect.TypeOf(&X{}).String())
fmt.Println(reflect.TypeOf(s1.Scanner{}).String())
fmt.Println(reflect.TypeOf(s2.Scanner{}).String())
fmt.Println(reflect.TypeOf(s1.Scanner{}).PkgPath(), reflect.TypeOf(s1.Scanner{}).String())
fmt.Println(reflect.TypeOf(s2.Scanner{}).PkgPath(), reflect.TypeOf(s2.Scanner{}).String())
}
int
main.X
*main.X
scanner.Scanner
scanner.Scanner
text/scanner scanner.Scanner
go/scanner scanner.Scanner

https://play.golang.org/p/NLODZNdik6r

有了这些信息,你可以(如果你觉得有这样的倾向(创建一个地图,让我们从一个reflect.Type到一个键,然后再回来,就像这样。

package main
import (
"fmt"
s2 "go/scanner"
"reflect"
s1 "text/scanner"
)
type TypeMap struct {
m []reflect.Type
}
func (m *TypeMap) Get(t reflect.Type) int {
for i, x := range m.m {
if x == t {
return i
}
}
m.m = append(m.m, t)
return len(m.m) - 1
}
func (m *TypeMap) Reverse(t int) reflect.Type {
return m.m[t]
}
type X struct{}
func main() {
var m TypeMap
fmt.Println(m.Get(reflect.TypeOf(1)))
fmt.Println(m.Reverse(0))
fmt.Println(m.Get(reflect.TypeOf(1)))
fmt.Println(m.Reverse(0))
fmt.Println(m.Get(reflect.TypeOf(1)))
fmt.Println(m.Reverse(0))
fmt.Println(m.Get(reflect.TypeOf(X{})))
fmt.Println(m.Reverse(1))
fmt.Println(m.Get(reflect.TypeOf(&X{})))
fmt.Println(m.Reverse(2))
fmt.Println(m.Get(reflect.TypeOf(s1.Scanner{})))
fmt.Println(m.Reverse(3).PkgPath(), m.Reverse(3))
fmt.Println(m.Get(reflect.TypeOf(s2.Scanner{})))
fmt.Println(m.Reverse(4).PkgPath(), m.Reverse(4))
}
0
int
0
int
0
int
1
main.X
2
*main.X
3
text/scanner scanner.Scanner
4
go/scanner scanner.Scanner

在上述情况下,我假设N很小。还要注意使用reflect.TypeOf的恒等式,它将在后续调用中返回相同类型的相同指针。

如果 N 不小,你可能想做一些更复杂的事情。

package main
import (
"fmt"
s2 "go/scanner"
"reflect"
s1 "text/scanner"
)
type PkgPathNum struct {
PkgPath string
Num     int
}
type TypeMap struct {
m map[string][]PkgPathNum
r []reflect.Type
}
func (m *TypeMap) Get(t reflect.Type) int {
k := t.String()
xs := m.m[k]
pkgPath := t.PkgPath()
for _, x := range xs {
if x.PkgPath == pkgPath {
return x.Num
}
}
n := len(m.r)
m.r = append(m.r, t)
xs = append(xs, PkgPathNum{pkgPath, n})
if m.m == nil {
m.m = make(map[string][]PkgPathNum)
}
m.m[k] = xs
return n
}
func (m *TypeMap) Reverse(t int) reflect.Type {
return m.r[t]
}
type X struct{}
func main() {
var m TypeMap
fmt.Println(m.Get(reflect.TypeOf(1)))
fmt.Println(m.Reverse(0))
fmt.Println(m.Get(reflect.TypeOf(X{})))
fmt.Println(m.Reverse(1))
fmt.Println(m.Get(reflect.TypeOf(&X{})))
fmt.Println(m.Reverse(2))
fmt.Println(m.Get(reflect.TypeOf(s1.Scanner{})))
fmt.Println(m.Reverse(3).PkgPath(), m.Reverse(3))
fmt.Println(m.Get(reflect.TypeOf(s2.Scanner{})))
fmt.Println(m.Reverse(4).PkgPath(), m.Reverse(4))
}
0
int
1
main.X
2
*main.X
3
text/scanner scanner.Scanner
4
go/scanner scanner.Scanner

https://play.golang.org/p/2fiMZ8qCQtY

请注意指向类型的指针的字幕,X*X实际上是不同的类型。

相关内容

  • 没有找到相关文章

最新更新