如何使用具有不可变值类型的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-net( http://code.google.com/p/protobuf-net/ )序列化程序一起使用时,我收到此错误:

无法对属性ConsoleApplication.Program + MyValueType.X应用更改

如果我将setter应用于标有DataMember属性的属性,它将起作用,但它会打破这种值类型的不变性,这对我们来说是不可取的。

有谁知道我需要做些什么才能让它发挥作用? 我注意到ProtoBu.Serializer.Serialize方法有一个重载,它接受一个SerializationInfo和一个StreamingContext,但是我没有在实现ISerializable接口的上下文之外使用它们,所以任何关于如何使用它们的代码示例这个背景将非常感谢!

谢谢,

编辑:所以我挖了一些旧的MSDN文章,并更好地了解了SerializationInfo和StreamingContext的使用位置和方式,但是当我尝试这样做时:

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

事实certificate, Serialize方法只允许引用类型,有特殊原因吗? 考虑到我能够序列化通过引用类型公开的值类型,这似乎有点奇怪。

您使用的是哪个版本的protobuf-net? 如果您是最新的v2版本,它应该自动处理。 如果我还没有部署此代码,我会稍后更新下载区域,但基本上如果您的类型是未加修饰的(没有属性),它将检测您正在使用的常见“元组”模式,并决定(来自构造函数) x (构造函数参数)/ X (属性)是字段1, z / Z是字段2。

另一种方法是标记字段:

 [ProtoMember(1)] private readonly int _x; [ProtoMember(2)] private readonly int _z; 

(或者字段上的[DataMember(Order=n)]

这应该工作,取决于信任级别。 我还没有做的是将构造函数代码概括为归因方案。 这并不难,但我想首先推动基本案例,然后进化。

我在这里添加了以下两个样本/测试和完整代码 :

  [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(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(ms); Assert.AreEqual(123, clone.X); Assert.AreEqual(456, clone.Z); } } 

也:

事实certificate,Serialize方法只允许引用类型

是的,这是与拳击模型等相关的v1的设计限制; 这不再适用于v2。

另请注意,protobuf-net本身不会消耗ISerializable (尽管它可用于实现 ISerializable )。