如何在序列化和反序列化期间保持引用相等性
本文关键字:引用 序列化 反序列化 | 更新日期: 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 };
}
当我运行此代码时,我得到True
和False
.我正在寻找可以使用的功能,这些功能可以给我带来True
和True
。
注意#1:我已经检查并看到了使用DataContractSerializer
和MarshalByRefObject
的参考,但我看不到如何将这些功能应用于这个问题;
注意#2:我知道我可以编写自己的自定义序列化逻辑,但我想避免这种情况,而是使用默认序列化。例如,如果我在 Java 中使用默认序列化,在这种情况下我会得到True
和True
,并且我正在寻找 .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;
在对Serailize
或Deserialize
的单独调用之间,引用身份永远不会被保留,除了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 确保两者都引用同一个对象。