1

我在 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:我已经检查并看到了使用DataContractSerializerand的引用MarshalByRefObject,但我没有看到如何将这些功能应用于这个问题;

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

4

2 回答 2

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;

Serailize在对or的单独调用之间永远不会保留引用标识Deserialize,除了IObjectReference- 但由于数组不实现IObjectReference,所以这是没有实际意义的。

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

  • 使用包装器对象,以便在需要保留引用的范围内只使用一个Serialize/调用Deserialize
  • 找到不依赖于此的替代设计

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

于 2013-09-15T08:17:56.733 回答
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 确保两者都引用同一个对象。

于 2013-09-15T07:53:08.477 回答