如何获得protobuf-net类的基类

时间:2012-09-25 06:35:04

标签: protobuf-net

我已经阅读了各种与继承有关的帖子,并且Protocol Buffers不支持继承。我不想在Protocol Buffers消息中继承,而是继承,所以我可以轻松处理所有的Protocol Buffers消息。

我正在使用protobuf-net 2.0.0.480和.proto文件来定义我的协议。这一切都运行良好,除非我到了一个共同的祖先,以便我可以做一些共同的功能,并允许轻松检查。一个简单的例子:

我的.proto文件:

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

最终为GFX_Layout_Advance生成类,GFX_Layout_Render为(仅为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 

因为它是一个部分类,似乎没有实现的可重写构造函数:

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

这将允许我将所有传入的消息视为GfxProtocolMessageBase并允许查询ProtocolInformation,以便我可以转换为适当的后代。在这种情况下GFX_Layout_Advance。然而.....

  • 添加额外的部分类GFX_Layout_Advance()会导致不同的protobuf编码。由于接口的唯一变化是一种方法我不明白为什么会这样?

底线是:

  1. 我想为所有生成的protobuf-net类引入一个共同的基础祖先
  2. 基本祖先类将允许我访问有关我正在处理的消息类型的信息,因为我不想在准备好之前强制转换为实际的消息类型
  3. 我如何实现1.& 2。?

    所有指针都赞赏。

1 个答案:

答案 0 :(得分:3)

  1. 是的,只要GfxProtocolMessageBase不是合同类型,它就可以正常工作。它故意使用部分类来允许这种类型的东西。编码数据不应该改变。如果你有一个我可以看到的行为不端的情景,我会很乐意调查。

  2. 那很好;只是:不要使用Serializer.Serialize<GfxProtocolMessageBase> / Serializer.Deserialize<GfxProtocolMessageBase>,因为序列化程序不应该知道关于GfxProtocolMessageBase(当然除非你很高兴,但那样做意味着你不会遵循现有的.proto 100%)。对于序列化,Serializer.NonGeneric.SerializetypeModel.Serialize(例如,RuntimeTypeModel.Default.Serialize)会自动执行正确的操作。要进行反序列化,您需要知道实际目标Type

  3. 当然,替代选项是将允许 GfxProtocolMessageBase作为基类型知道序列化程序,并使用protobuf-net的内置继承支持([ProtoInclude(...)]等等 - 但问题是:它不会将100%映射到你的.proto,因为继承是作为封装实现的(通过protobuf-net),这意味着:它将被编写为好像它是一个基本消息可选的子消息字段数。


    编辑以显示从单个流中读取不同对象(异构类型)的类型解析器用法:

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