Ответ 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]
(в этом случае тело вспомогательного сообщения пуст)
Это покрывает это?