1

我有一个使用接口作为键的地图。地图是这样定义的MyMap map[Signature]Packets。接口是Signature,并且将有两个结构AB实现这个接口。我也msgp用来序列化这两个结构。

我的问题是msgp自动生成使用指针作为函数接收器类型的方法,我认为这会使键Signature接收指针。如果是这种情况,那么键每次都会不同,因为指针不同,即使基础值相同。因此,每次,我都会创建一个新条目,而不是找到现有条目并对其进行修改。

我想知道:

  1. 有没有办法强制msgp使用具体类型的函数接收器来生成方法?目前,我只能将自动生成的方法的函数接收器修改为MarshalMsg具体UnmarshalMsg类型(AB代替*A*B)。通过这样做,地图的键是 typeA或 type B,并且地图MyMap工作正常。但是,我知道我不应该修改自动生成的代码。所以,我想知道是否有一种可接受的方式来做到这一点。
  2. 如果没有办法做 1.,是否有任何解决方法来解决这个问题?我真的需要地图键的一些多态特性,使用msgp.

更新 1(4 月 12 日):感谢您分享您的想法并提供解决方案。以下是关于我的问题的一些细节。

  1. 背景是地图用于收集不同的网络事件。实现接口的两个结构SignatureEventSignatureIPv4EventSignatureIPv6
type EventSignatureIPv4 struct {
    SourceIPv4 [4]byte
    Port     uint16
    Traffic  TrafficType
}

type EventSignatureIPv6 struct {
    SourceIPv6 [16]byte
    Port     uint16
    Traffic  TrafficType
}

并且Signature持有在 IPv4 和 IPv6 数据之间共享的通用方法。因此,本质上,我想在运行时收集和分组相应的 IPv4/v6 事件。地图的关键是识别同一个源,地图的价值是收集不同目的地的事件。

  1. msgp我正在使用的库是这个https://pkg.go.dev/github.com/tinylib/msgp@v1.1.5/msgp

  2. 如果我错了,请纠正我。对于 Go 中的组合,如果方法集中的方法之一具有指针类型的函数接收器,那么实例只会是指针类型吗?所以在这里,就像我一样

func (z *EventSignatureIPv6) MarshalMsg(b []byte) (o []byte, err error) {
    /* Auto-generated code */
}

每当我Signature用来接收结构EventSignatureIPv6时,结构只会是类型*EventSignatureIPv6

4

1 回答 1

2

你是对的,“如果它们指向同一个变量,两个指针值是相等的。” ,所以如果你想比较可能持有指向不同类型的指针的接口,例如*Aand *B,你已经遇到了麻烦。

话虽如此,我认为首先使用接口类型作为映射键并不是一个了不起的想法,因为您必须处理一些警告,首先是:

必须为键类型的操作数完全定义比较运算符 == 和 !=

现在您需要注意实现接口的类型。理论上,没有人会阻止客户端在具有底层不可散列类型的已定义类型上实现您的接口,例如type UncomparableSignature []int

因此,您可能必须在接口上添加一个未导出的方法,以便该包之外的客户端代码无法实现它。但是,没有什么能阻止同一个包中的代码实现它,所以这充其量只是维护开销。

然后,如果接口持有指向零值的指针,它甚至取决于规范的实现:

指向不同的零大小变量的指针可能相等,也可能不相等。

此外,您还会遇到讨厌的错误,例如Signature持有 a的类型变量nil会覆盖彼此的值:

var foo Signature
var bar Signature

myMap[foo] = &Packet{/*pretending to have value 1*/}
myMap[bar] = &Packet{/*pretending to have value 2*/}

fmt.Println(myMap[foo]) // 2

一个可能的解决方案是,您可以用唯一的 id 替换映射键,并通过在接口上声明适当的方法来强制实现者提供它Signature(这仍然假设可以协调实现者以在所有这些上提供唯一的 id) :

type Signature interface {
    UniqueIdent() uint64 // or string, if you prefer
    // ...other methods
}

接着

packet := myMap[someSignature.UniqueIdent()]
于 2021-04-12T12:53:33.483 回答