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

本文关键字:引用 序列化 反序列化 | 更新日期: 2023-09-27 18:31:17

我在 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 中的类似工具。

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

这根本不会发生。任何引用保留语义仅对 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 - 我见过太多的人丢失数据或陷入混乱,通常是当他们在代码版本之间迭代时。它不太容易改变。

编辑:不可能进行所需的比较,因为 ReferenceEqual 实际上是用来检查它是否是同一个对象。从 msdn

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

因此,如果您想拥有ReferenceEqual,那么唯一的方法就是我可以按照我在下面描述的那样进行操作。

原始帖子

在第一次检查时,由于这些陈述,您得到 true

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 确保两者都引用同一个对象。