Что означает атрибут ProtoInclude (в protobuf-net) - PullRequest
18 голосов
/ 04 июня 2009

В реализации 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 Ответ

23 голосов
/ 12 июня 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

...