Что означает атрибут ProtoInclude (в protobuf-net)

В ProtoBuf-Net, что означает атрибут ProtoInclude и что он делает?

Пример будет оценен.

Я видел это в этом сообщении, и я не уверен, что он делает. Пример:

[Serializable,
 ProtoContract,
 ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
  [ProtoMember(1)]
  abstract public UInt16 messageType { get; }
}

[Serializable,
 ProtoContract]
internal class BeginRequest : BaseMessage
{
    [ProtoMember(1)]
    public override UInt16 messageType
    {
        get { return 1; }
    }
}

Кроме того, существует ли способ генерации такого наследования с помощью инструмента protogen?

Ответы

Ответ 1

Извините, я не хотел пропустить этот - увы, я не вижу всего.

Учитывая специфику вопроса, я собираюсь предположить, что вы, по крайней мере, знакомы с .proto; исправьте меня, если я ошибаюсь.

[ProtoInclude] работает так же, как [XmlInclude] для XmlSerializer - или [KnownType] для DataContractSerializer - позволяет распознавать подклассы типа во время (де) сериализации. Единственное, что нужно, это тег (номер), чтобы идентифицировать каждый подтип (который должен быть уникальным и не сталкиваться ни с одним из полей родительского типа).

Re protogen: nope; базовая спецификация (google) вообще не предусматривает наследование, поэтому протоген (через .proto) не имеет механизма для выражения этого. protobuf-net обеспечивает поддержку наследования как расширение, но делает это таким образом, что все еще оставляет сообщения проводными, совместимыми с другими реализациями. Возможно, я мог бы добавить поддержку протонов через новые свойства расширения в спецификации google, но я еще не сделал этого.

Итак, посмотреть на пример; который выражает отношения наследования между BaseMessage и BeginRequest; независимо от того, выполняете ли вы:

Serialize<BaseMessage>(...)
Serialize<BeginRequest>(...)
  • в любом случае, он будет начинаться с базы (BaseMessage) и работать вверх; что не совсем верно - он записывает данные, начиная с BeginRequest (так что он знает, что мы имеем BeginRequest как можно раньше во время десериализации). Важно то, что поля из любых типов контрактов с родителями включены, и сериализатор просматривает фактический объект, переданный в него, а не только тип, который вы говорите.

Аналогично, во время deserilaization, независимо от того, используете ли вы:

Deserialize<BaseMessage>(...)
Deserialize<BeginRequest>(...)

вы получите тип, который вы фактически сериализовали (предположительно a BeginRequest).

Под капотом для целей совместимости (с широким спецификацией буферов протоколов) это похоже на запись чего-то вроде (простить любые ошибки, мое .proto - ржавое):

message BaseMessage {
    optional BeginRequest beginRequest = 50;
    optional uint32 messageType = 1;   
}
message BeginRequest {        
}

(переопределение, вероятно, не должно указывать [ProtoMember], btw.

Как правило, он записывает поля в порядке возрастания тегов, но для эффективной десериализации двигатель настойчиво выбирает сначала записывать данные подкласса (что явно разрешено спецификацией) - то есть он пишет что-то вроде (у вас будет представить себе двоичный файл...):

[tag 50, string][length of sub-message][body of sub-message][tag 1, int][value]

(в этом случае тело вспомогательного сообщения пуст)

Это покрывает это?