gpt4 book ai didi

.net - 解决实现 ISerializable 的对象的循环引用

转载 作者:行者123 更新时间:2023-12-01 11:59:56 24 4
gpt4 key购买 nike

我正在编写自己的 IFormatter 实现,但我想不出一种方法来解决两种都实现 ISerializable 的类型之间的循环引用。

这是通常的模式:

[Serializable]
class Foo : ISerializable
{
private Bar m_bar;

public Foo(Bar bar)
{
m_bar = bar;
m_bar.Foo = this;
}

public Bar Bar
{
get { return m_bar; }
}

protected Foo(SerializationInfo info, StreamingContext context)
{
m_bar = (Bar)info.GetValue("1", typeof(Bar));
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("1", m_bar);
}
}

[Serializable]
class Bar : ISerializable
{
private Foo m_foo;

public Foo Foo
{
get { return m_foo; }
set { m_foo = value; }
}

public Bar()
{ }

protected Bar(SerializationInfo info, StreamingContext context)
{
m_foo = (Foo)info.GetValue("1", typeof(Foo));
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("1", m_foo);
}
}

然后我这样做:

Bar b = new Bar();
Foo f = new Foo(b);
bool equal = ReferenceEquals(b, b.Foo.Bar); // true

// Serialise and deserialise b

equal = ReferenceEquals(b, b.Foo.Bar);

如果我使用开箱即用的 BinaryFormatter 对 b 进行序列化和反序列化,则上述引用相等性测试会像预期的那样返回 true。但是我想不出在我的自定义 IFormatter 中实现此目的的方法。

在非 ISerializable 情况下,一旦目标引用已被解析,我可以简单地使用反射重新访问“待定”对象字段。但是对于实现 ISerializable 的对象,不可能使用 SerializationInfo 注入(inject)新数据。

谁能指出我正确的方向?

最佳答案

这种情况是FormatterServices.GetUninitializedObject的原因方法。一般的想法是,如果你有对象 A 和 B,它们在它们的 SerializationInfo 中相互引用,你可以按如下方式反序列化它们:

(出于此解释的目的,(SI,SC) 指的是类型的反序列化构造函数,即采用 SerializationInfoStreamingContext 的构造函数。)

  1. 首先选择一个对象进行反序列化。只要您不选择值类型,您选择哪种都无关紧要。假设您选择 A。
  2. 调用 GetUninitializedObject分配(但不初始化)A 类型的实例,因为您还没有准备好调用它的 (SI,SC) 构造函数。
  3. 以通常的方式构建 B,即创建一个 SerializationInfo 对象(它将包括对现在反序列化一半的 A 的引用)并将其传递给 B 的 (SI,SC) 构造函数。
  4. 现在您拥有了初始化分配的 A 对象所需的所有依赖项。创建它的 SerializationInfo 对象并调用 A 的 (SI,SC) 构造函数。您可以通过反射在现有实例上调用构造函数。

GetUninitializedObject 方法是纯粹的 CLR 魔术 - 它创建一个实例而无需调用构造函数来初始化该实例。它基本上将所有字段设置为零/空。

这就是警告您不要在 (SI,SC) 构造函数中使用子对象的任何成员的原因 - 子对象可能已分配但此时尚未初始化.这也是 IDeserializationCallback 接口(interface)的原因,它使您有机会在保证完成所有对象初始化之后和返回反序列化对象图之前使用您的子对象。

ObjectManager class 可以为您完成所有这些(以及其他类型的修复)。但是,考虑到反序列化的复杂性,我总是发现它的文档很少,所以我从来没有花时间去尝试弄清楚如何正确使用它。它使用一些更神奇的方法来执行第 4 步,使用一些经过优化的内部到 CLR 反射来更快地调用 (SI,SC) 构造函数(我已经将它计时的速度大约是公共(public)方式)。

最后,有些对象图涉及无法反序列化的循环。一个例子是当你有一个由两个 IObjectReference 实例组成的循环时(我已经测试了 BinaryFormatter 并且它抛出了一个异常)。另一个我怀疑是如果你有 cycle involving nothing but boxed value-types .

关于.net - 解决实现 ISerializable 的对象的循环引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2712393/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com