protobuf-net inheritance

♀尐吖头ヾ 提交于 2019-12-17 18:25:03

问题


Marc mentioned on stackoverflow that it will be possible in v2 of protobuf-net to use ProtoInclude attribute (or similar approach) to serialize/deserialize class hierarchy without a need to specify each subtype in the base class. Is this implemented yet? We have a plugin interface that can be derived in external libraries, so there is no way of knowing what the derived types will be. We could maintain unique numbering between types though, but I couldn’t find any examples on the net, short of using ProtoInclude attribute which requires a subtype to be specified.

How would I go about implementing inheritance with protobuf-net like that if I don't know what the subtypes are?


回答1:


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.




回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/6247513/protobuf-net-inheritance

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