4

継承に関するさまざまな投稿を読みましたが、ProtocolBuffersは継承をサポートしていません。Protocol Buffersメッセージに継承は必要ありませんが、継承が必要なので、すべてのProtocolBuffersメッセージを簡単に処理できます。

プロトコルを定義するためにprotobuf-net2.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を達成するにはどうすればよいですか?

すべてのポインタを高く評価しました。

4

1 に答える 1

3
  1. GfxProtocolMessageBaseはい、契約タイプでない限り、問題なく動作するはずです。このタイプのものを許可するために、意図的に部分クラスを使用します。エンコードされたデータは変更しないでください。私が見ることができる誤動作のシナリオがある場合は、喜んで調査します。

  2. それはいいです; ただ:シリアライザーが知らないはずなので、Serializer.Serialize<GfxProtocolMessageBase>/を使用しないでください(もちろん、満足している場合を除きますが、それは、既存の100%に従わないことを意味します)。シリアル化の場合、または(たとえば)のいずれかが自動的に正しいことを行います。デシリアライズの場合、実際のターゲットを知る必要があります。Serializer.Deserialize<GfxProtocolMessageBase>GfxProtocolMessageBase.protoSerializer.NonGeneric.SerializetypeModel.SerializeRuntimeTypeModel.Default.SerializeType

もちろん、代替オプションは、シリアライザーにベースタイプとして認識され、protobuf-netの組み込み継承サポート(など)を使用できるよう にすることですが、問題は、.protoに100%マップされないことです。 、継承はカプセル化として(protobuf-netによって)実装されるため、つまり、オプションのサブメッセージフィールドがいくつかあるベースメッセージであるかのように書き込まれます。GfxProtocolMessageBase[ProtoInclude(...)]


編集して、単一のストリームから(異種タイプの)さまざまなオブジェクトを読み取るためのタイプリゾルバーの使用法を示します。

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);
            }
        }
    }
}
于 2012-09-25T17:19:01.740 に答える