ProtoMessage 方法的目的是什么?

thu*_*urt 8 go protocol-buffers

当我从协议缓冲区文件生成 go 代码时,我注意到每个生成的结构都实现了 Message 接口,https://github.com/golang/protobuf/blob/master/proto/lib.go#L277

有关生成的代码的示例,请参阅https://github.com/google/go-genproto/blob/master/googleapis/rpc/status/status.pb.go#L97

显然,Message 接口上的其他方法 String() 和 Reset() 具有明显的目的,具体的实现示例说明了这一点。但是,我不明白 ProtoMessage() 方法的目的。该方法不接受任何参数,也不返回任何参数,那么为什么会出现这种情况呢?

icz*_*cza 6

引自官方文档:Protocol Buffers: Go Generated Code:

给定一个简单的消息声明:

message Foo {}
Run Code Online (Sandbox Code Playgroud)

协议缓冲区编译器生成一个名为Foo. A*Foo实现了Message接口。有关更多信息,请参阅内嵌注释。

type Foo struct {
}

// Reset sets the proto's state to default values.
func (m *Foo) Reset()         { *m = Foo{} }

// String returns a string representation of the proto.
func (m *Foo) String() string { return proto.CompactTextString(m) }

// ProtoMessage acts as a tag to make sure no one accidentally implements the
// proto.Message interface.
func (*Foo) ProtoMessage()    {}
Run Code Online (Sandbox Code Playgroud)

请注意,所有这些成员始终都在场;该optimize_for选项不会影响 Go 代码生成器的输出。

这是 Go 官方常见问题解答中描述的(类似)技术:如何保证我的类型满足接口?

如果您希望接口的用户明确声明他们实现了它,您可以将具有描述性名称的方法添加到接口的方法集中。例如:

type Fooer interface {
    Foo()
    ImplementsFooer()
}
Run Code Online (Sandbox Code Playgroud)

然后,类型必须将ImplementsFooer方法实现为 a Fooer,清楚地记录事实并在godoc的输出中宣布它。

type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}
Run Code Online (Sandbox Code Playgroud)

大多数代码不使用这种约束,因为它们限制了接口思想的效用。但有时,它们对于解决相似接口之间的歧义是必要的。

所以这个ProtoMessage()方法基本上有两个目的:

  • 主要目的如生成的代码中所述:确保您没有传递 protobuf 序列化/反序列化的值,否则(没有此方法)将实现proto.Message接口但不是“真正的”protobuf 消息值:

    ProtoMessage 作为一个标签来确保没有人意外地实现了 proto.Message 接口。

  • 它还记录了类型是 a (implements) 的事实Message。刚开始生成代码时,这可能没有真正的价值,您不应该过多打扰它,但是代码分析器和 Go IDE 可能会很好地利用它;它也出现在生成的文档中。