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

How to choose a field number when using protobuf-net inheritance?

我正在使用 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;
        }
    }
}

除以下要求外没有其他要求:

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

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