2

BinaryFormatter の代わりに protobuf-net を使用するようにプロジェクトを変換します。ドキュメントが不足しているようですhttp://code.google.com/p/protobuf-net/w/list http://code.google.com/p/protobuf-net からいくつかの例も調べました/source/browse/ しかし、私にはまだ明確でないことがいくつかあります。そのため、ここで質問することにしました。

1. ISerializable と Serializer.Merge/Serialize について

特定のシリアル化を行うために ISerializable から継承している場合。私が読んだように: ProtoBuf-Net ProtoInclude Generic Type Subclass フック Serializer.Merge/Serialize; を使用する必要があります。

クラスがあるとします:

[Serializable]
[ProtoContract]
public class Anchor : ISerializable
{       
    [ProtoMember(1)]
    public int id;

    [ProtoMember(2)]
    public Ship ship;
    ...
 }

Serializer.Merge(情報、 これ); コンストラクターAnchor(SerializationInfo info, StreamingContext context)に追加する必要があります

および Serializer.Serialize(info, this); void GetObjectData(SerializationInfo info, StreamingContext context) に追加

したがって、次のようになります。

    protected Anchor(SerializationInfo info, StreamingContext context)
    {
        //for binaryformatter:            
        Type myType = typeof(Anchor);
        foreach (SerializationEntry e in info)
        {
            FieldInfo f = myType.GetField(e.Name,BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Public|BindingFlags.Instance);
            f.SetValue(this,e.Value);
        }

        //added for protobuf-net:
        Serializer.Merge(info, this);
   }

    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        //for binaryformatter:
        foreach (FieldInfo f in typeof(Anchor).GetFields(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance))
        {
            if ((f.Attributes&FieldAttributes.NotSerialized)==0)
                info.AddValue(f.Name,f.GetValue(this));
        }

       //added for protobuf-net:
        Serializer.Serialize(info, this);
    }

質問: これは正しいですか? (「情報」はシリアライザーによって上書きされますか?つまり、バイナリフォーマッターは正しく機能しませんか?現時点では、protobuf-netを使用しようとしていますが、バイナリフォーマッターも適切に動作するようにしたいと思います)

2. ProtoInclude と RuntimeTypeModel.Default の使い方について

クラスのベースとなるクラス Messageがあるとします:クラス Ack、クラス HandshakeClientInfo... クラス Command。私が読んだように、メッセージの子をシリアライズしたい場合: protobuf - net の [ProtoInclude(1, "MyClass")] は機能しませんでした 。コンパイル時に子の型についてわかっていれば、問題ありません。

(別のプロジェクトにあるため) コンパイル時に判別できない型の子には、RuntimeTypeModel.Default[typeof(Message)].AddSubType(207, typeof(Command));を使用する必要があります。 または Type.AssemblyQualifiedName を使用するには: [ProtoInclude(207, "Trainer.Commands.Command, Simulator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]

[Serializable]
[ProtoContract]
[ProtoInclude(200, typeof(Ack))]
[ProtoInclude(201, typeof(HandshakeClientInfo))]
[ProtoInclude(202, typeof(HandshakeReadyToSimulation))]
[ProtoInclude(203, typeof(FileMessage))]
[ProtoInclude(204, typeof(HandshakeResponse))]
[ProtoInclude(205, typeof(Sync))]
[ProtoInclude(206, typeof(HandshakeSimulationStart))]    

public abstract class Message {

    [ProtoMember(1)]
    public byte Sender;
...
}

私は protobuf-net v2 (r580) とRuntimeTypeModel.Default[typeof(Message)].AddSubType(207, typeof(Command));のバリアントを使用します。の方が好ましいようです。

質問: コード内のどこに配置すればよいかわかりません。コンストラクターまたは.... ? また、 ProtoIncludeRuntimeTypeModel.Defaultを一緒に使用することはできますか?

4

1 に答える 1

5

まず、protobuf-net と の関係を明確にする必要がありISerializableます。まず、ISerializableによってのみ使用されBinaryFormatterます。protobuf-net はこのインターフェイスを探すことはなく、このインターフェイスを直接使用することもありません。では、なぜ protobuf-net がこれについて言及しているのかと疑問に思うかもしれません。

回答: 一部の人々は、を使用する必要BinaryFormatterがある既存のコードを持っていますが、内部で別のものを使用したいと考えています。この良い例は、リモート処理を使用する既存のコードです。ここで、protobuf-net を使用して を実装し ISerializable、基本的に の内臓を置き換えることができますBinaryFormatter。一般的な使用方法は次のとおりです。

protected Anchor(SerializationInfo info, StreamingContext context)
{
    Serializer.Merge(info, this);
}

public virtual void GetObjectData(SerializationInfo info,
    StreamingContext context)
{
    Serializer.Serialize(info, this);
}

これは内部で protobuf-net を使用します。protobuf-net はオブジェクト (およびすべての子データ) をシリアル化し、データを 1 つのフィールドに格納します。逆シリアル化中に、これが逆になります。インターフェイスは、ISerializable追加情報を protobuf-net に追加するために使用されません。ほとんどの場合、 protobuf-netとを別々にサポートしたい場合、 constructor/ に /protobuf-net コードはありません。また、既にある (またはない)既存の実装に変更を加える必要もありません。 )。これは、内部からの動作を変更したい非常に特定のシナリオのみです BinaryFormatterSerializerGetObjectDataISerializableBinaryFormatter. そのシナリオでは、通常、完全な制御を protobuf-net に渡します。


サブタイプに関するご質問について; [ProtoInclude]はい、と組み合わせて使用​​できますRuntimeTypeModel.Default。基本的に、デフォルト (調整可能) では、protobuf-net が初めて型を検出すると、属性がチェックされます。存在する場合は、それらの属性を使用してモデルを構成します。ただし、そのタイプをシリアル化/逆シリアル化する必要があるまでは、構成に必要な変更を加えることができます。その時点で、構成情報が作業を行うための戦略に焼き付けられます。戦略を決定したら、構成を変更することは好きではありません。

したがって、モデルを構成する最適なタイミングは、アプリの起動時です。または、少なくとも、何か面白いことを始める前に。完全を期すために、モデルを完全に手動で構成する場合は、属性を無視するように依頼することもできます (これは非常にまれです)。

だからあなたの行:

RuntimeTypeModel.Default[typeof(Message)].AddSubType(207, typeof(Command));

アプリの起動時に問題ありません。

(まれな) いくつかのケースでは、新しい型を後で発見することがあります。さまざまな非常に複雑な理由から、シリアル化を開始した後に既存のモデルを変更できるようにすることはあまり現実的ではありませんが、:RuntimeTypeModel.Default単なる既定のインスタンスです。いくつかのより高度なシナリオでは、独自のモデルを維持し、必要に応じてより知識のある新しいモデルを構成することをお勧めします。したがって、 を使用する代わりに、次を使用できます。RuntimeTypeModel.Default

static RuntimeTypeModel serializer = TypeModel.Create();

その後、新しいセットアップを構成できます。

var workInProgress = TypeModel.Create();
// TODO: add whatever you need here
serializer = workInProgress;
于 2012-09-22T20:01:59.140 に答える