наследование protobuf-net - PullRequest
11 голосов
/ 06 июня 2011

Марк упомянул в stackoverflow, что в версии 2 protobuf-net будет возможно использовать атрибут ProtoInclude (или аналогичный подход) для сериализации / десериализации иерархии классов без необходимости указывать каждый подтип в базовом классе. Это уже реализовано? У нас есть интерфейс плагина, который может быть получен во внешних библиотеках, поэтому нет никакого способа узнать, какими будут производные типы. Хотя мы могли бы поддерживать уникальную нумерацию между типами, но я не смог найти никаких примеров в сети, кроме использования атрибута ProtoInclude, который требует указания подтипа.

Как бы я реализовал наследование с помощью protobuf-net, если не знаю, что это за подтипы?

Ответы [ 3 ]

18 голосов
/ 06 июня 2011

Если вы не можете указать подтипы в атрибутах (поскольку они не известны во время компиляции), у вас есть 2 варианта (оба из которых применяются только к «v2», доступному как бета-версия):

  1. использовать RuntimeTypeModel вместо статических Serializer методов (которые теперь просто сокращены до RuntimeTypeModel.Default); рассказать модели о наследовании (пример ниже)
  2. добавить DynamicType = true к [ProtoMember(...)] в вопросе

Второй - не очень чистый протобуф - он встраивает информацию о типах, которую я на самом деле не люблю , но люди просто хранят просить. Первый мой предпочтительный вариант. Чтобы добавить подтипы во время выполнения:

var model = TypeModel.Create();
var type = model.Add(typeof(YourBaseType), true);
var subTypeA = model.Add(typeof(SomeSubType), true);
var subTypeB = model.Add(typeof(SomeOtherSubType), true);
type.AddSubType(4, typeof(SomeSubType));
type.AddSubType(5, typeof(SomeOtherSubType));

true в вышеприведенном означает «использовать обычные правила для автоматического добавления свойств элементов» - вы также можете взять это под свой контроль и указать свойства (и т. Д.) Вручную, если хотите.

Обратите внимание, что TypeModel следует кэшировать и использовать повторно (не создавать для объекта, который необходимо сериализовать), поскольку он включает в себя некоторый код "emit" для генерации методов. Повторное использование будет быстрее и потребует меньше памяти. Модель типов является поточно-ориентированной и может использоваться для сериализации / десериализации нескольких потоков одновременно в разных потоках.

5 голосов
/ 14 ноября 2012

Чтобы еще больше расширить ответ Марка, конкретно касающийся RuntimeTypeModel, это один из способов написать его:

RuntimeTypeModel.Default[typeof(BaseClass)].AddSubType(20, typeof(DerivedClass));

Если у вас есть больше классов, производных от производных классов, свяжите их так:

RuntimeTypeModel.Default[typeof(DerivedClass)].AddSubType(20, typeof(DerivedFromDerivedClass ));

и т. Д.
Затем вы можете использовать Serializer.Serialize(file,object), как это обычно делается с protobuf-net.
Это работает для всех проектов и пространств имен.

2 голосов
/ 24 сентября 2013

Добавив вспомогательный метод расширения:

public static class RuntimeTypeModelExt
{
    public static MetaType Add<T>(this RuntimeTypeModel model)
    {
        var publicFields = typeof(T).GetFields().Select(x => x.Name).ToArray();
        return model.Add(typeof(T), false).Add(publicFields);
    }
}

Вы можете упростить регистрацию подтипов следующим образом:

private static RuntimeTypeModel CreateModel()
{
    var model = TypeModel.Create();
    model.Add<ExportInfo>();
    model.Add<RegistrationInfo>();
    model.Add<FactorySetupInfo>()
        .AddSubType(101, model.Add<ServiceSetupInfo>().Type)
        .AddSubType(102, model.Add<GenericWrapperSetupInfo>().Type)
        .AddSubType(103, model.Add<DecoratorSetupInfo>().Type);
    return model;
}
...