I have read the various posts to do with inheritance and that Protocol Buffers does not support inheritance. I don’t want inheritance in the Protocol Buffers messages but rather inheritance so I can deal with all my Protocol Buffers messages easily.
I am using protobuf-net 2.0.0.480 and a .proto file to define my protocol. It all works well except when I get to the point where I would like a common ancestor so that I can do some common functionality 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 a portion of GFX_Layout_Advance) shown:
[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
As it was a partial class and there appeared to be no overrideable constructor I implemented:
public partial class GFX_Layout_Advance : GfxProtocolMessageBase
{
public override ProtocolInformation ProtocolInformation()
{
return ProtocolInfo;
}
}
This would allow me to treat all incoming messages as GfxProtocolMessageBase and allow querying of ProtocolInformation so that I can cast to the appropriate descendant. In this case GFX_Layout_Advance. However…..
- Adding the additional partial class GFX_Layout_Advance() causes a different protobuf encoding. As the only change to the interface is a method I don’t understand why this is?
Bottom line is:
- I want to introduce a common base ancestor to all the generated protobuf-net classes
- Base ancestor class will allow me access to information about what kind of message I am dealing with as I don’t want to have to cast to the actual message type until I am ready
How do I achieve 1. & 2.?
All pointers appreciated.
Yes that should work fine as long as
GfxProtocolMessageBaseis not a contract type. It uses partial classes deliberately to allow this type of thing. The encoded data should not change. If you have a scenario that misbehaves that I can look at I will happily investigate.That’s fine; just: don’t use
Serializer.Serialize<GfxProtocolMessageBase>/Serializer.Deserialize<GfxProtocolMessageBase>, as the serializer should not know aboutGfxProtocolMessageBase(unless of course you’re happy to, but that does mean you won’t be following the existing.proto100%). For serialization, eitherSerializer.NonGeneric.SerializeortypeModel.Serialize(for example,RuntimeTypeModel.Default.Serialize) will do the right thing automatically. For deserialization, you will need to know the actual targetType.Of course, the alternative option is to allow
GfxProtocolMessageBaseto be known to the serializer as a base-type, and use protobuf-net’s inbuilt inheritance support ([ProtoInclude(...)]etc) – but the problem then is: that won’t map 100% to your .proto, as inheritance is implemented (by protobuf-net) as encapsulation, meaning: it will be written as though it is a base-message with a number of optional sub-message fields.Edit to show type-resolver usage for reading different objects (of heterogeneous types) from a single stream: