2011-08-19 8 views
10

Giả sử tôi có một kiểu giá trị bất biến như thế này:Làm thế nào để sử dụng protobuf-net với các loại giá trị bất biến?

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

Nó hoạt động với BinaryFormatter hoặc DataContractSerializer nhưng khi tôi cố gắng sử dụng nó với protobuf-net (http://code.google.com/p/protobuf-net/) serializer tôi nhận được lỗi này:

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

Nếu tôi áp dụng setters cho các thuộc tính được đánh dấu với thuộc tính DataMember nó sẽ làm việc nhưng sau đó nó phá vỡ bất biến của loại giá trị này và đó là không mong muốn cho chúng tôi.

Có ai biết tôi cần làm gì để nó hoạt động không? Tôi đã nhận thấy rằng có một quá tải của phương thức ProtoBu.Serializer.Serialize lấy một SerializationInfo và một StreamingContext nhưng tôi đã không sử dụng chúng bên ngoài bối cảnh triển khai thực hiện giao diện ISerializable, vì vậy bất kỳ ví dụ mã nào về cách sử dụng chúng trong bối cảnh này sẽ được nhiều người đánh giá cao!

Cảm ơn,

EDIT: vì vậy tôi đào lên một số bài viết cũ MSDN và có một sự hiểu biết tốt hơn về địa điểm và cách SerializationInfo và StreamingContext được sử dụng, nhưng khi tôi đã cố gắng để làm điều này:

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

nó chỉ ra rằng các phương pháp Serialize<T> chỉ cho phép các loại tài liệu tham khảo, là có một lý do cụ thể cho điều đó? Có vẻ hơi lạ khi tôi có thể sắp xếp các loại giá trị được hiển thị thông qua loại tham chiếu.

+0

Xin lỗi vì sự chậm trễ - cuối tuần bận rộn –

Trả lời

10

Bạn đang sử dụng phiên bản protobuf-net nào? Nếu bạn là phiên bản v2 mới nhất, nó sẽ tự động đối phó với điều này. Trong trường hợp tôi chưa triển khai mã này, tôi sẽ cập nhật các khu vực tải xuống trong giây lát, nhưng về cơ bản nếu loại của bạn chưa được sắp xếp (không có thuộc tính), nó sẽ phát hiện ra "tuple" chung mà bạn đang sử dụng và quyết định (từ các nhà xây dựng) mà x (constructor tham số)/X (tài sản) là lĩnh vực 1, và z/Z là lĩnh vực 2.

Một cách khác là để đánh dấu các lĩnh vực:

[ProtoMember(1)] 
private readonly int _x; 

[ProtoMember(2)] 
private readonly int _z; 

(hoặc cách khác [DataMember(Order=n)] trên các trường)

nên ork, tùy thuộc vào mức độ tin cậy. Những gì tôi chưa hoàn thành đã được tổng quát hóa mã trình xây dựng cho các kịch bản được phân bổ. Đó không phải là khó khăn, nhưng tôi muốn đẩy trường hợp cơ bản đầu tiên, sau đó phát triển nó.

Tôi đã thêm hai mẫu sau đây/kiểm tra with full code here:

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

Ngoài ra:

it turns out that the Serialize method only allows reference types

có, đó là một giới hạn thiết kế của v1 có liên quan đến mô hình đấm bốc vv; điều này không còn áp dụng với v2.

Ngoài ra, lưu ý rằng protobuf-net không tự tiêu thụ ISerializable (mặc dù nó có thể được sử dụng để thực hiện ISerializable).

+0

+1 Câu trả lời hay, gần như là một bài đăng :-) –

+0

Xin chào Marc, vừa tải xuống các tệp nhị phân mới bạn đã đăng và nó hoạt động, tôi cũng đã kiểm tra tuần tự/deserializing bằng cách sử dụng một thể hiện của RuntimeTypeModel và họ cũng làm việc. – theburningmonk

+0

Nhưng tôi tò mò mặc dù, tại sao phương pháp RuntiemTypeModel.Deserialize cần một cá thể để sửa đổi khi nó trả về đối tượng đã sửa đổi. Trong trường hợp của các loại giá trị bất biến có nghĩa là bạn sẽ làm một cái gì đó dọc theo dòng: var clone = (MyValueType) FormatterServices.GetUninitializedObject (typeof (MyValueType)); clone = (MyValueType) runtimeTypeModel.Deserialize (memStream, clone, typeof (MyValueType)); mà chỉ cảm thấy không hoàn toàn đúng – theburningmonk