如何在序列化和反序列化期间保持引用相等性

时间:2013-09-15 07:30:55

标签: c# .net serialization

我在C#/ .NET中遇到序列化的问题,如果我在一个流中序列化多个对同一对象的引用,则在反序列化后这些引用不再相等。我正在使用默认的二进制序列化。绊倒我的代码是:

Check ck1 = new Check();
Check ck2 = new Check();
ck1.Numbers = new int[] { 11, 12, 13 };
ck2.Numbers = ck1.Numbers;
Console.WriteLine(ReferenceEquals(ck1.Numbers, ck2.Numbers));
FileStream fs = new FileStream("d:\\deleteme-check3.txt", FileMode.Create, FileAccess.Write);
BinaryFormatter oos = new BinaryFormatter();
oos.Serialize(fs, ck1);
oos.Serialize(fs, ck2);
fs.Flush();
fs.Close();
fs = new FileStream("d:\\deleteme-check3.txt", FileMode.Open, FileAccess.Read);
oos = new BinaryFormatter();
Check ck3 = (Check)oos.Deserialize(fs);
Check ck4 = (Check)oos.Deserialize(fs);
Console.WriteLine(ReferenceEquals(ck3.Numbers, ck4.Numbers));

宣言是

[Serializable]
class Check
{
  public int[] Numbers = new int[] { 0, 1, 2 };
}

当我运行此代码时,我得到TrueFalse。我正在寻找可以使用的功能TrueTrue

注意#1:我已经查看并查看了使用DataContractSerializerMarshalByRefObject的引用,但我没有看到如何将这些功能应用于此问题;

注意#2:我知道我可以编写自己的自定义序列化逻辑,但我想避免使用它,而是使用默认序列化。例如,如果我在Java中使用默认序列化,那么在这种情况下我会得到TrueTrue,我正在寻找.NET中的类似工具。

2 个答案:

答案 0 :(得分:3)

这根本不会发生。任何引用保留语义仅对单个Serialize / Deserialize的调用有效。为了获得你想要的东西,你需要使用某种包装器,即

[Serializable]
public class HazTwo {
   public Check First {get;set;}
   public Check Second {get;set;}
}

然后序列化:

var obj = new HazTwo { First = ck1, Second = ck2 };
oos.Serialize(fs, obj);

和反序列化:

var newObj = (HazTwo)oos.Deserialize(fs);
var ck3 = newObj.First;
var ck4 = newObj.Second;

SerailizeDeserialize的单独调用之间不会保留引用标识,但IObjectReference略有例外 - 但由于数组未实现IObjectReference ,这是没有实际意义的。

坦率地说,我怀疑你最好建议:

  • 使用包装器对象,因此只需要对需要保留引用的范围进行单Serialize / Deserialize次调用
  • 找到一个不依赖于此
  • 的替代设计

我还应该添加脚注,我通常会建议人们不要过度使用BinaryForamtter - 我看到太多人丢失数据或陷入混乱,通常是他们在代码版本之间进行迭代。它不太适合改变。

答案 1 :(得分:0)

编辑: 不可能进行你想要的比较,因为ReferenceEqual实际上是要检查它是同一个对象。来自msdn

  

与Equals方法和相等运算符不同   ReferenceEquals方法无法重写。因此,如果你   想要测试两个对象引用是否相等而且不确定   执行Equals方法,可以调用   ReferenceEquals方法。但是,请注意,如果objA和objB是值   类型,它们在传递给ReferenceEquals之前被装箱   方法

因此,如果您想要使用ReferenceEqual,那么就可以按照下面的描述进行操作。

原帖

在第一次检查时,你会因为这些陈述而成真

ck1.Numbers = new int[] { 11, 12, 13 };
ck2.Numbers = ck1.Numbers;

第二行使得ck2引用与ck1相同的对象。

关于反序列化,你可以

Check ck3 = (Check)oos.Deserialize(fs);
Check ck4 = (Check)oos.Deserialize(fs);

你在这里做的是创建两个新对象,ck3和ck4引用不同的对象。要获得与序列化之前相同的结果,请执行

Check ck3 = (Check)oos.Deserialize(fs);
Check ck4 = ck3;

这里创建了一个新对象ck3,ck4 = ck3确保两者都引用同一个对象。