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

Posted

技术标签:

【中文标题】使用protobuf-net继承时如何选择字段号?【英文标题】:How to choose a field number when using protobuf-net inheritance? 【发布时间】:2015-03-26 14:36:43 【问题描述】:

我正在使用 protobuf-net 序列化许多类型,其中一些是从基本类型继承的。我知道 Protocol Buffers 规范不支持继承,因此 protobuf-net 中的支持基本上是一种解决方法。

我没有使用 protobuf-net 属性,而是配置了自定义 RuntimeTypeModel,并使用了 AddAddSubType 方法。我不太明白我应该如何确定将哪些数字用于传递给AddSubType 方法的字段编号(也就是将在ProtoInclude 属性中使用的数字)。

This SO question 和其他几个类似的并没有真正描述如何选择字段编号,实际上我已经看到了许多不同的变化:4 和 5; 7 & 8; 101 & 102 & 103; 20; 500;等等。显然他们被选中是为了不互相冲突,但是如何他们是被选中的?什么决定了从哪个数字开始?

以下代码是一个人为的示例,但它确实符合我的层次结构(具有两个派生子类型的基本 Event 类型)。

using System;
using System.Collections.Generic;
using ProtoBuf.Meta;

namespace Test

    public sealed class History
    
        public History()
        
            Events = new List<Event>();
        

        public ICollection<Event> Events  get; private set; 
    

    public enum EventType
    
        ConcertStarted, ConcertFinished, SongPlayed
    

    public class Event
    
        public EventType Type  get; set; 
        public DateTimeOffset Timestamp  get; set; 
    

    public sealed class Concert : Event
    
        public string Location  get; set; 
    

    public sealed class Song : Event
    
        public string Name  get; set; 
    

    public static class ModelFactory
    
        public static RuntimeTypeModel CreateModel()
        
            RuntimeTypeModel model = TypeModel.Create();
            model.Add(typeof(DateTimeOffset), applyDefaultBehaviour: false)
                .SetSurrogate(typeof(DateTimeOffsetSurrogate));
            model.Add(typeof(History), applyDefaultBehaviour: false)
                .Add("Events");
            model.Add(typeof(Concert), applyDefaultBehaviour: false)
                .Add("Location");
            model.Add(typeof(Song), applyDefaultBehaviour: false)
                .Add("Name");
            model.Add(typeof(Event), applyDefaultBehaviour: false)
                .Add("Type", "Timestamp")
                .AddSubType(???, typeof(Concert))
                .AddSubType(???, typeof(Song));
            return model;
        
    

【问题讨论】:

【参考方案1】:

没有其他要求:

它们必须是正整数 他们不能冲突 它们必须可靠地重复(重要的是,无论您重新启动应用程序多少次,即使您添加了其他类型等,子类型和数字都必须匹配)

除此之外:没关系。留出空隙可能会更容易向父类型添加其他字段而不会意外产生冲突,但是:较小的字段编号序列化成本更低,因此如果可能:更喜欢小数字

【讨论】:

当您说“它们不能冲突”时,我是否说子类型字段编号必须大于基类型中的最高字段编号?那么如果基类型有字段号 1 和 2,那么子类型的字段号必须大于 2 吗?而且您是否还说子类型字段编号必须都是唯一的?例如,如果我有从 A 派生的类型 A1 和 A2,从 B 派生的 B1 和 B2,那么 A1、A2、B1 和 B2 必须都有不同的子类型字段编号? @StevenRands 不,只是不同;子类型编号可以是 1、3、5,字段可以是 2、4、6 - 当然,这会令人困惑。对于类型 X,X 的 字段 和 X 的 立即子类型 共享一个 protobuf 范围,因此这些数字不能重叠 - 但是,如果 Y 是X 的子类型,X 和 Y 之间对字段编号没有限制 - 它们是完全独立的。所以 X 可以有字段 1,2,3(并且 Y 作为带有标记 4 的子类型),并且 Y 可以有字段 1,2,3,4 啊,现在明白了!感谢您的澄清。

以上是关于使用protobuf-net继承时如何选择字段号?的主要内容,如果未能解决你的问题,请参考以下文章

不支持 Protobuf-NET IExtensible 继承的解决方法

如何停止使用 Protobuf-Net 继承,直接使用继承类?

关于继承的 ProtoBuf-Net 的问题?

如何通过 ProtoBuf-net 使用 DateTimeKind 选项序列化 DateTime 字段

Protobuf-net 中的继承:ProtoInclude 和兼容性

具有代理和继承层次结构的 protobuf-net v3