2011-08-19 13 views
10

私はこのような不変の値型を持っていると仮定します。protobuf-netを不変の値型で使用する方法は?

[Serializable] 
[DataContract] 
public struct MyValueType : ISerializable 
{ 
private readonly int _x; 
private readonly int _z; 

public MyValueType(int x, int z) 
    : this() 
{ 
    _x = x; 
    _z = z; 
} 

// this constructor is used for deserialization 
public MyValueType(SerializationInfo info, StreamingContext text) 
    : this() 
{ 
    _x = info.GetInt32("X"); 
    _z = info.GetInt32("Z"); 
} 

[DataMember(Order = 1)] 
public int X 
{ 
    get { return _x; } 
} 

[DataMember(Order = 2)] 
public int Z 
{ 
    get { return _z; } 
} 

public static bool operator ==(MyValueType a, MyValueType b) 
{ 
    return a.Equals(b); 
} 

public static bool operator !=(MyValueType a, MyValueType b) 
{ 
    return !(a == b); 
} 

public override bool Equals(object other) 
{ 
    if (!(other is MyValueType)) 
    { 
     return false; 
    } 

    return Equals((MyValueType)other); 
} 

public bool Equals(MyValueType other) 
{ 
    return X == other.X && Z == other.Z; 
} 

public override int GetHashCode() 
{ 
    unchecked 
    { 
     return (X * 397)^Z; 
    } 
} 

// this method is called during serialization 
public void GetObjectData(SerializationInfo info, StreamingContext context) 
{ 
    info.AddValue("X", X); 
    info.AddValue("Z", Z); 
} 

public override string ToString() 
{ 
    return string.Format("[{0}, {1}]", X, Z); 
} 
} 

それはにBinaryFormatterまたはDataContractSerializerで動作しますが、私がいるProtobufネット(http://code.google.com/p/protobuf-net/)シリアライザとそれを使用しようとすると、私はこのエラーを取得:

Cannot apply changes to property ConsoleApplication.Program+MyValueType.X

DataMember属性でマークされたプロパティにsetterを適用すると、それは機能しますが、この値型の不変性が破られ、それは私たちにとって望ましくありません。

誰かが私がそれを稼働させるために必要なことを知っていますか?私は、SerializationInfoとStreamingContextを取り込むProtoBu.Serializer.Serializeメソッドのオーバーロードがあることに気付いていますが、ISerializableインターフェイスを実装するコンテキスト外では使用していないので、それらを使用する方法に関するコード例この文脈は非常に高く評価されるでしょう!

おかげで、

編集:私はいくつかの古いMSDNの記事を掘っ、どこで、どのようにしたSerializationInfoとStreamingContextが使用されているのより良い理解を得たが、私はこれを行うにしようとしたとき:

var serializationInfo = new SerializationInfo(
    typeof(MyValueType), new FormatterConverter()); 
ProtoBuf.Serializer.Serialize(serializationInfo, valueType); 

Serialize<T>メソッドで参照型のみが許可されていることが判明しましたが、それには特別な理由がありますか?参照型で公開されている値の型をシリアル化することができれば、ちょっと変わったようです。

+0

- 多忙な週末 –

答えて

10

あなたはどのバージョンのprotobuf-netを使用していますか?最新のv2ビルドの場合は、自動的にこれに対処する必要があります。このコードをまだ配備していない場合は、ダウンロードエリアを一瞬で更新しますが、基本的にあなたのタイプが未装飾(属性なし)であれば、使用している一般的なタプルパタンを検出し、

[ProtoMember(1)] 
private readonly int _x; 

[ProtoMember(2)] 
private readonly int _z; 

(あるいは[DataMember(Order=n)]x(コンストラクタパラメータ)/ X(プロパティ)がフィールド1であり、そしてz/Zフィールド2.別のアプローチは、フィールドをマークすることである

あること)コンストラクタからフィールド上)

ork、信頼レベルに応じて。私がにまだを実行していないと、帰納的シナリオにコンストラクタコードが一般化されます。それは難しいことではありませんが、基本的なケースを先に進めてから進化させたいと思っていました。

また
[Test] 
    public void RoundTripImmutableTypeAsTuple() 
    { 
     using(var ms = new MemoryStream()) 
     { 
      var val = new MyValueTypeAsTuple(123, 456); 
      Serializer.Serialize(ms, val); 
      ms.Position = 0; 
      var clone = Serializer.Deserialize<MyValueTypeAsTuple>(ms); 
      Assert.AreEqual(123, clone.X); 
      Assert.AreEqual(456, clone.Z); 
     } 
    } 
    [Test] 
    public void RoundTripImmutableTypeViaFields() 
    { 
     using (var ms = new MemoryStream()) 
     { 
      var val = new MyValueTypeViaFields(123, 456); 
      Serializer.Serialize(ms, val); 
      ms.Position = 0; 
      var clone = Serializer.Deserialize<MyValueTypeViaFields>(ms); 
      Assert.AreEqual(123, clone.X); 
      Assert.AreEqual(456, clone.Z); 
     } 
    } 

:私は、次の2つのサンプル/テストwith full code hereを追加しました

it turns out that the Serialize method only allows reference types

はい、それはなどボクシングのモデルに関連したV1の設計上の制限でした。これはもはやv2には適用されません。また

は、(それが ISerializableを実装 に使用することができる)ISerializableを消費しない自体ないこといるProtobufネットに注意してください。遅れて申し訳ありません

+0

1グレート答え、 –

+0

こんにちはマルク・:-)ほとんどのポスト、ちょうど私もRuntimeTypeModelのインスタンスを使用してデシリアライズ/シリアライズチェックしました、あなたが投稿新しいバイナリをダウンロードし、それが働きました彼らも働く。 – theburningmonk

+0

しかし、私は不思議ですが、理由はRuntiemTypeModel.Deserializeメソッドは、変更されたオブジェクトを返すときに変更するインスタンスを必要とするためです。不変な値の型の場合は、次の行に沿って何かをすることになります。var clone =(MyValueType)FormatterServices.GetUninitializedObject(typeof(MyValueType));クローン=(MyValueType)runtimeTypeModel.Deserialize(memStream、クローン、typeof(MyValueType));これはちょうどいい感じではありません – theburningmonk

関連する問題