protobuf-net inheritance

混江龙づ霸主 提交于 2019-11-28 06:59:56

If you can't specify the subtypes in attributes (because it isn't known at compile-time) you have 2 options (both of which only apply to "v2", available as beta):

  1. use a RuntimeTypeModel, rather than the static Serializer methods (which are now just a short-cut to RuntimeTypeModel.Default); tell the model about the inheritance (example below)
  2. add DynamicType = true to the [ProtoMember(...)] in question

The second is not very pure protobuf - it embeds type information, which I don't really love but people just kept asking for. The first is my preferred option. To add subtypes at runtime:

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));

the true in the above means "use normal rules to add member properties automatically" - you can also take control of that and specify the properties (etc) manually if you prefer.

Note that a TypeModel should be cached and re-used (not created per object you need to serialize), as it includes some "emit" code to generate methods. Re-using it will be faster and require less memory. The type-model is thread-safe, and can be used to serialize/deserialize multiple streams concurrently on different threads.

To further expand Marc's answer, specifically dealing with RuntimeTypeModel, this is one way to write it:

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

If you have more classes derived from derived classes, link them like this

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

And so on.
You can then use Serializer.Serialize(file,object), as you would normally with protobuf-net.
This works across projects and namespaces.

By adding helper extension method:

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);
    }
}

You can streamline sub-type registration like this:

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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!