2011-08-19 18 views
10

varsayalım:Protobuf-net, değişmez değer tipleri ile nasıl kullanılır? Böyle bir değişmez değer türüne sahip

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

O BinaryFormatter veya DataContractSerializer ile çalışır ama protobuf-net ile kullanmaya çalıştığınızda (http://code.google.com/p/protobuf-net/) seri hale getirici bu hatayı alıyorum:

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

DataMember özniteliğiyle işaretlenen özelliklere set uygularsam işe yarayacaktır, ancak bu değer tipinin değişmezliğini bozar ve bu bizim için arzu edilmez.

Çalışmak için ne yapmam gerektiğini bilen var mı? Ben bir SerializationInfo ve bir StreamingContext alır, ancak bunları ISerializable arabirimini uygulama bağlam dışında kullanmıyorum ProtoBu.Serializer.Serialize yöntemi bir aşırı yük olduğunu fark ettim, bu nedenle bunları kullanmak için herhangi bir kod örnekleri Bu bağlam çok takdir edilecektir!

sayesinde

DÜZENLEME: bu yüzden bazı eski MSDN makalesine kazılıp ve SerializationInfo ve StreamingContext kullanılır ve nasıl daha iyi anlaşılmasını var, ama bunu yapmak için çalıştım:

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

Serialize<T> yönteminin yalnızca referans türlerine izin verdiğini ortaya çıkarır, bunun için belirli bir neden var mı? Referans tipi ile maruz bırakılan değer tiplerini serileştirebileceğim için biraz garip görünüyor.

+0

Gecikme için üzgünüz - telaşlı bir hafta sonu –

cevap

10

Hangi protobuf-net sürümünü kullanıyorsunuz? En son v2 yapısıysanız, otomatik olarak bununla baş etmelisiniz. Bu kodu henüz kurmadıysam, indirme alanlarını bir an sonra güncelleyeceğim, ancak türünüzün sayısı değişmemişse (öznitelik yok), kullandığınız ortak "tuple" paternini algılayacak ve karar verecek (

[ProtoMember(1)] 
private readonly int _x; 

[ProtoMember(2)] 
private readonly int _z; 

(ya da alternatif olarak [DataMember(Order=n)]: x (kurucu parametresi)/X (özelliği) alanı 1 ve z/Z alanı 2. Diğer bir yaklaşım alanları işaretlemek için

olduğu) kurucuda alanlarda)

w gerekir Güven seviyesine bağlı olarak ork. Ne henüz yapmamış, yapıcı kodu ilişkilendirilmiş senaryolara genelleme. Bu zor değil, ama önce temel durumu zorlamak istedim, sonra evrim geçirdim. o boks modeline vb ilgili v1 bir tasarım sınırlaması idi

it turns out that the Serialize method only allows reference types

evet;: Aşağıdaki iki örnek ekledik

/with full code here testleri: Ayrıca

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

Bu artık v2 ile geçerli değil. Ayrıca

, (o ISerializable uygulamak için kullanılabilir rağmen) ISerializable tüketmek değil kendisi olmadığını protobuf-net not edin.

+0

+1 Harika bir yanıt, neredeyse bir gönderi :-) –

+0

Merhaba Marc, yeni gönderdiği ikili dosyaları yeni yükledim ve işe yaradı, ayrıca RuntimeTypeModel örneğini kullanarak serileştirme/serileştirmeyi kontrol ettim ve onlar da çalışıyorlar. – theburningmonk

+0

Ama yine de meraklıyım, RuntiemTypeModel.Deserialize yönteminin neden değiştirilmiş nesneyi döndürdüğünü değiştirmek için bir örneğe gereksinim duyduğunu merak ediyorum. Değişmez değer türleri söz konusu olduğunda, aşağıdaki satır boyunca bir şey yapmanıza gerek kalmaz: var clone = (MyValueType) FormatterServices.GetUninitializedObject (typeof (MyValueType)); clone = (MyValueType) runtimeTypeModel.Deserialize (memStream, clone, typeof (MyValueType)); Bu sadece – theburningmonk