How to get base class for my protobuf-net classes

I read various messages related to inheritance, and that protocol buffers do not support inheritance. I do not want inheritance in protocol buffer messages, but rather inheritance, so I can easily process all messages with protocol buffers.

I am using protobuf-net 2.0.0.480 and the .proto file to determine my protocol. All this works well, except when I get to the point where I need a common ancestor so that I can perform some common functions and allow for easy inspection. A simple example:

My .proto file:

message ProtocolInformation { enum MessageKinds { LAYOUT_ADVANCE = 1; LAYOUT_RENDER = 2; } required MessageKinds MessageKind = 1; required int32 UniqueID = 2; } message GFX_Layout_Advance { required ProtocolInformation ProtocolInfo = 1; required int32 LayoutHandle = 2; } message GFX_Layout_Render { required ProtocolInformation ProtocolInfo = 1; required int32 LayoutHandle = 2; required int32 Stage = 3; } 

which ultimately generates classes for GFX_Layout_Advance, GFX_Layout_Render as (only part of GFX_Layout_Advance):

 [global::System.Serializable, global::ProtoBuf.ProtoContract(Name = @"GFX_Layout_Advance")] public partial class GFX_Layout_Advance : global::ProtoBuf.IExtensible { public GFX_Layout_Advance() { } private GFX_Protocol.ProtocolInformation _ProtocolInfo; [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name = @"ProtocolInfo", DataFormat = global::ProtoBuf.DataFormat.Default)] public GFX_Protocol.ProtocolInformation ProtocolInfo 

Since it was a partial class, and there seemed to be no redefinable constructor that I implemented:

 public partial class GFX_Layout_Advance : GfxProtocolMessageBase { public override ProtocolInformation ProtocolInformation() { return ProtocolInfo; } } 

This will allow me to process all incoming messages as GfxProtocolMessageBase and allow the protocol request so that I can use the corresponding child. In this case, GFX_Layout_Advance. But.....

  • Adding an additional partial class GFX_Layout_Advance () leads to a different encoding of the protobuff. Since the only interface change is the method, I do not understand why this is?

Bottom line:

  • I want to introduce a common base ancestor to all the generated protobuf-net classes.
  • The base ancestor class will allow me to access information about which message I'm dealing with, because I don't want me to need to pass the actual message type until I'm ready.

How to reach 1. and 2 ..

All pointers appreciated.

+4
source share
1 answer
  • Yes, this should work fine until GfxProtocolMessageBase is a contract type. He deliberately uses partial classes to resolve such things. The encoded data should not change. If you have a script that doesn’t work well, I can happily study it.

  • It's good; simple: do not use Serializer.Serialize<GfxProtocolMessageBase> / Serializer.Deserialize<GfxProtocolMessageBase> , since the serializer should not know about GfxProtocolMessageBase (unless, of course, you are happy, but that means that you will not follow the existing .proto 100%). For serialization, either Serializer.NonGeneric.Serialize or typeModel.Serialize (e.g. RuntimeTypeModel.Default.Serialize ) will do the right thing automatically. For deserialization, you need to know the actual purpose of the Type .

Of course, an alternative is to let GfxProtocolMessageBase be known to the serializer as the base type and use the built-in inheritance support of protobuf-net ( [ProtoInclude(...)] , etc.), but the problem is that it won Don't match 100% with your .proto, since inheritance is implemented (protobuf-net) as encapsulation, that is: it will be written as if it were a base message with a number of additional sub-message fields.


Change to show the use of a resolver type for reading different objects (heterogeneous types) from one stream:

 using ProtoBuf; using System; using System.Collections.Generic; using System.IO; [ProtoContract] class Foo { [ProtoMember(1)] public int Id { get; set; } public override string ToString() { return "Foo with Id=" + Id; } } [ProtoContract] class Bar { [ProtoMember(2)] public string Name { get; set; } public override string ToString() { return "Bar with Name=" + Name; } } static class Program { // mechanism to obtain a Type from a numeric key static readonly Dictionary<int, Type> typeMap = new Dictionary<int, Type> { {1,typeof(Foo)}, {2,typeof(Bar)} }; static Type ResolveType(int key) { Type type; typeMap.TryGetValue(key, out type); return type; } static void Main() { // using MemoryStream purely for convenience using (var ms = new MemoryStream()) { // serialize some random data (here I'm coding the outbound key // directly, but this could be automated) Serializer.SerializeWithLengthPrefix(ms, new Foo { Id = 123 }, PrefixStyle.Base128, 1); Serializer.SerializeWithLengthPrefix(ms, new Bar { Name = "abc" }, PrefixStyle.Base128, 2); Serializer.SerializeWithLengthPrefix(ms, new Foo { Id = 456 }, PrefixStyle.Base128, 1); Serializer.SerializeWithLengthPrefix(ms, new Bar { Name = "def" }, PrefixStyle.Base128, 2); // rewind (this wouldn't be necessary for a NetworkStream, // FileStream, etc) ms.Position = 0; // walk forwards through the top-level data object obj; while (Serializer.NonGeneric.TryDeserializeWithLengthPrefix( ms, PrefixStyle.Base128, ResolveType, out obj)) { // note we overrode the ToString on each object to make // this bit work Console.WriteLine(obj); } } } } 
+3
source

Source: https://habr.com/ru/post/1436069/


All Articles