【问题标题】:How do I get a base class for my protobuf-net classes如何为我的 protobuf-net 类获取基类
【发布时间】:2012-09-25 06:35:04
【问题描述】:

我已经阅读了有关继承的各种帖子,并且 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. 基础祖先类将允许我访问有关我正在处理的消息类型的信息,因为在我准备好之前我不想强制转换为实际的消息类型

我如何实现 1. 和 2.?

感谢所有指针。

【问题讨论】:

    标签: protobuf-net


    【解决方案1】:
    1. 是的,只要GfxProtocolMessageBase 不是合同类型,它就可以正常工作。它故意使用部分类来允许这种类型的事情。编码数据不应改变。如果您有我可以查看的行为不端的场景,我会很乐意进行调查。

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

    当然,另一种选择是允许 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);
                }
            }
        }
    }
    

    【讨论】:

    • 1.谢谢。我还有一些其他错误导致我看到的意外序列化。不是 protobuf-net 问题。 Delphi 反序列化程序正在成功使用序列化。 2. 仍然需要从 Delphi 序列化的内容反序列化,但不要指望这是一个问题。有了以上内容,我已经能够填充 .proto 100%,这正是我想要的。
    • 对于反序列化,您需要知道目标类型....不幸的是,我不知道那会是什么,因为有很多消息都来自同一个输入流。如何让 protobuf-net 根据检索到的字节自动创建一个?
    • @TheEdge 协议缓冲区规范没有 inbuilt 方法来处理它。但是,这里的技巧是使用SerializeWithLengthPrefix(output, obj, PrefixStyle.Base128, fieldNumber) 写入此数据,其中fieldNumber 是每个类型的——即Foo 可能是1,Bar 可能是2,等等。然后使用NonGeneric.TryDeserializeWithLengthPrefix(input, PrefixStyle.Base128, typeResolver, out obj),其中@987654338 @ 返回 Foo 为 1,Bar 为 2,等等。如果你愿意,我可以做一个完整的例子。
    • @Mark 如果您有一些简单的示例代码,我将不胜感激。我如何处理这个 Delphi 方面是,给定一个有效的协议缓冲区字节流,我的基类能够解码消息 ProtocolInformation {},然后从该 MessageKind 告诉我我需要什么类型进行解码。有什么办法可以在 C# 方面做类似的事情吗?那么使用 protobuf-net 对字节流进行必要的“窥视”以获得我需要的实际类型?
    • @TheEdge 通过 TryDeserializeWithLengthPrefix 在 .NET 术语中添加了示例。重新执行与您的 Delphi 实现相同的操作:您能更具体吗? protobuf-net 在原始阅读器 API 上没有“窥视”功能,但是:一个原始阅读器 API,因此将原始阅读器用于外部部分可能是合理的,并且ProtoReader.ReadObject 用于内部结构。我必须确切地看到你的数据看起来像什么,我可能需要公开 keys - 但基本上所有部分都在那里。
    猜你喜欢
    • 1970-01-01
    • 2013-01-03
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多