Protobuf-net - 如何使用 oneof

Posted

技术标签:

【中文标题】Protobuf-net - 如何使用 oneof【英文标题】:Protobuf-net - How to use oneof 【发布时间】:2021-10-20 14:14:16 【问题描述】:

我快速搜索了Protobuf-netoneof 的用法,它似乎是supported as of v2.3.0,但我一生都找不到任何关于如何的例子有人会使用它!

我的要求很简单,也许这也可以用[ProtoInclude] 解决,但我不太确定这到底是如何工作的。我有以下课程:

[ProtoContract]
public class ProgressUIMessage

    [ProtoMember(1)]
    public int Id get; set;

    [ProtoMember(2)]
    public object Message get; set;

Message 可以是 8 种不同的已知类型中的一种。这些类型根本不相互继承,虽然代码可以更改,但所有类型都没有共同点。

使用Google.Protobuf 我希望做一些事情similar to this,其中我有一个名为Instrument 的属性,它可以是上面示例中的两种类型之一,然后使用InstrumentOneofCase 来确定我的类型被给予。但是我如何在Protobuf-net 中实现同样的目标?

编辑: 我将保留最初的问题,但也许更多人可以涉及的更好的问题是:您将如何实现与 Protobuf-net 中的this MS example 相同的事情?无论是在编写类本身方面还是在确定参数最终是什么具体类型方面?

【问题讨论】:

【参考方案1】:

解决这个问题的方法是从您引用的 MS 示例中获取消息,并通过 protogen 运行它以查看它的作用 - 我们可以在这里非常方便地做到这一点:https://protogen.marcgravell.com/(注意我正在添加 @ 987654322@在文件顶部,MS示例中省略)。

除其他外,这给了我们:

    [global::ProtoBuf.ProtoMember(2, Name = @"stock")]
    public Stock Stock
    
        get => __pbn__instrument.Is(2) ? ((Stock)__pbn__instrument.Object) : default;
        set => __pbn__instrument = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
    
    public bool ShouldSerializeStock() => __pbn__instrument.Is(2);
    public void ResetStock() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__instrument, 2);

    private global::ProtoBuf.DiscriminatedUnionObject __pbn__instrument;

    [global::ProtoBuf.ProtoMember(3, Name = @"currency")]
    public Currency Currency
    
        get => __pbn__instrument.Is(3) ? ((Currency)__pbn__instrument.Object) : default;
        set => __pbn__instrument = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
    
    public bool ShouldSerializeCurrency() => __pbn__instrument.Is(3);
    public void ResetCurrency() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__instrument, 3);

所以我们可以看到它基本上使用了建立在DiscriminatedUnionObject 类型之上的条件序列化。实际上有一堆名为 DiscriminatedUnion* 的相关类型 - 取决于您需要重叠的内容,但由于它们都是这里的消息类型:DiscriminatedUnionObject 适合我们。


还有一个可选的“oneof 应该使用枚举”选项(奇怪的是,“选项”下),如果启用,还会添加:

    public InstrumentOneofCase InstrumentCase => (InstrumentOneofCase)__pbn__instrument.Discriminator;

    public enum InstrumentOneofCase
    
        None = 0,
        Stock = 2,
        Currency = 3,
    

否则,您将不得不使用ShouldSerialize*() 方法来解决活动案例。

希望这能阐明 oneof 如何与 protobuf-net 一起使用。

【讨论】:

感谢 Marc,但您能否解释一下如何使用 protobuf-net 中的属性编写该类?我的意思是我想有比这更简单的方法:) @cogumel0 最终,这里的目标之一是允许重叠存储,这样我们就不会拥有臃肿的数据类型,而做到这一点的唯一方法是通过DiscriminatedUnion* 的工作方式;您可以手动对其进行编码,但如上所示的代码广泛是使用 protobuf-net 实现“oneof”的预期方式;我想不出任何其他满足预期语义的好方法;如果可以的话:我全神贯注! @MarcGravell 如果我们获取 protogen 生成的完整代码,然后在其上调用 Serializer.GetProto<ChangeNotification>,那么结果是否应该再次与 MS 示例中的原始 .proto 代码完全相同,或者一点也不? (如果需要,我可以提出一个新问题)。 @evilmandarine 它至少意味着与协议兼容; IIRC 我目前没有任何明确的方式在 GetProto/annotations 中表示 oneof;如果我们这样做会很好,作为补充

以上是关于Protobuf-net - 如何使用 oneof的主要内容,如果未能解决你的问题,请参考以下文章

如何在运行时确定设置的 oneof 字段的名称?

生成 Spring 代码时如何将 OpenAPI“oneOf”属性与 openapi-generator-maven-plugin 一起使用

使用protobuf-net继承时如何选择字段号?

如果子消息没有字段,如何在 protobuf 消息上分配 oneof 字段?

如何使用 protobuf-net 扩展?

如何使用 protobuf-net 读回附加的对象?