通过递归引用了解 ProtoBuf-Net AsReference

发布于 2024-12-01 05:09:42 字数 3087 浏览 0 评论 0原文

我正在尝试 ProtoMember 中的 AsReference 选项进行递归引用。如果我使用公共构造函数创建 AnOwner,然后序列化/反序列化,AnOwner.Data 将变为 null。有人可以解释内部发生的情况以及是否支持递归引用吗?谢谢!

[ProtoContract()]
public class SomeData
{
    [ProtoMember(1, AsReference = true)]
    public AnOwner Owner;

    [ProtoMember(2)]
    public string Value;

    /// <summary>
    /// ProtoBuf deserialization constructor. In fact, Serializer did not complain when this is missing
    /// </summary>
    private SomeData()
    {

    }

    public SomeData(string value)
    {
        Value = value;
    }
}

[ProtoContract()]
public class AnOwner
{
    [ProtoMember(1)]
    public SomeData Data;

    /// <summary>
    /// ProtoBuf deserialization constructor
    /// </summary>
    private AnOwner()
    {

    }

    public AnOwner(SomeData data)
    {
        Data = data;
        Data.Owner = this;
    }
}

编辑: 经过深思熟虑,我设法以这个小演示的形式理解它,我将在这里分享。对于当前的实现(v2 beta),对于将哪个对象传递到 Serializer.Serialize() 而言,是否为两者指定 AsReference=true、两者都不指定或其中之一指定都很重要。

public class Program
{
    using System.IO;
    using ProtoBuf;
    using System;

    public static void main();
    {
        AnOwner owner1, owner2;
        AnOwner owner = new AnOwner();
        SomeData data = new SomeData();
        owner.Data = data;
        data.Owner = owner;
        string file = "sandbox.txt";

        try { File.Delete(file); } catch {}; // Just in case, cos' it felt like some caching was in place.

        using (var fs = File.OpenWrite(file)) { Serializer.Serialize(fs, owner); }
        using (var fs = File.OpenRead(file)) { owner1 = Serializer.Deserialize<AnOwner>(fs); }
        using (var fs = File.OpenRead(file)) { owner2 = Serializer.Deserialize<AnOwner>(fs); }

        Console.WriteLine("SomeData.i: {0}, {1}, {2}, {3}", owner1.Data.i, owner1.Data.Owner.Data.i, owner2.Data.i, owner2.Data.Owner.Data.i);
        Console.WriteLine("AnOwner.i: {0}, {1}, {2}, {3}", owner1.i, owner1.Data.Owner.i, owner2.i, owner2.Data.Owner.i);

        System.Diagnostics.Debug.Assert(owner1 == owner1.Data.Owner, "1. Expect reference same, but not the case.");
        System.Diagnostics.Debug.Assert(owner2 == owner2.Data.Owner, "2. Expect reference same, but not the case.");
        System.Diagnostics.Debug.Assert(owner1 != owner2, "3. Expect reference different, but not the case.");
    }
}

[ProtoContract()]
public class SomeData
{
    public static readonly Random RAND = new Random(2);

    [ProtoMember(1, AsReference = true)]
    public AnOwner Owner;

    // Prove that SomeData is only instantiated once per deserialise
    public int i = RAND.Next(100);

    public SomeData() { }
}

[ProtoContract()]
public class AnOwner
{
    public static readonly Random RAND = new Random(3);

    [ProtoMember(1, AsReference=true)]
    public SomeData Data;

    // Prove that AnOwner is only instantiated once per deserialise
    public int i = RAND.Next(100);

    /// <summary>
    /// ProtoBuf deserialization constructor
    /// </summary>
    public AnOwner() { }
}

I am trying out the AsReference option in ProtoMember for recursive referencing. If I create AnOwner with the public constructor and then serialize/deserialize, AnOwner.Data becomes null. Can someone explain what happens internally and whether recursive referencing is supported? Thanks!

[ProtoContract()]
public class SomeData
{
    [ProtoMember(1, AsReference = true)]
    public AnOwner Owner;

    [ProtoMember(2)]
    public string Value;

    /// <summary>
    /// ProtoBuf deserialization constructor. In fact, Serializer did not complain when this is missing
    /// </summary>
    private SomeData()
    {

    }

    public SomeData(string value)
    {
        Value = value;
    }
}

[ProtoContract()]
public class AnOwner
{
    [ProtoMember(1)]
    public SomeData Data;

    /// <summary>
    /// ProtoBuf deserialization constructor
    /// </summary>
    private AnOwner()
    {

    }

    public AnOwner(SomeData data)
    {
        Data = data;
        Data.Owner = this;
    }
}

EDIT:
After much deliberation, I manage to understand it in the form of this small demo which I will share here. With the current implementation (v2 beta) it matters if AsReference=true is specified for both, neither or either with respect to which object is passed into Serializer.Serialize().

public class Program
{
    using System.IO;
    using ProtoBuf;
    using System;

    public static void main();
    {
        AnOwner owner1, owner2;
        AnOwner owner = new AnOwner();
        SomeData data = new SomeData();
        owner.Data = data;
        data.Owner = owner;
        string file = "sandbox.txt";

        try { File.Delete(file); } catch {}; // Just in case, cos' it felt like some caching was in place.

        using (var fs = File.OpenWrite(file)) { Serializer.Serialize(fs, owner); }
        using (var fs = File.OpenRead(file)) { owner1 = Serializer.Deserialize<AnOwner>(fs); }
        using (var fs = File.OpenRead(file)) { owner2 = Serializer.Deserialize<AnOwner>(fs); }

        Console.WriteLine("SomeData.i: {0}, {1}, {2}, {3}", owner1.Data.i, owner1.Data.Owner.Data.i, owner2.Data.i, owner2.Data.Owner.Data.i);
        Console.WriteLine("AnOwner.i: {0}, {1}, {2}, {3}", owner1.i, owner1.Data.Owner.i, owner2.i, owner2.Data.Owner.i);

        System.Diagnostics.Debug.Assert(owner1 == owner1.Data.Owner, "1. Expect reference same, but not the case.");
        System.Diagnostics.Debug.Assert(owner2 == owner2.Data.Owner, "2. Expect reference same, but not the case.");
        System.Diagnostics.Debug.Assert(owner1 != owner2, "3. Expect reference different, but not the case.");
    }
}

[ProtoContract()]
public class SomeData
{
    public static readonly Random RAND = new Random(2);

    [ProtoMember(1, AsReference = true)]
    public AnOwner Owner;

    // Prove that SomeData is only instantiated once per deserialise
    public int i = RAND.Next(100);

    public SomeData() { }
}

[ProtoContract()]
public class AnOwner
{
    public static readonly Random RAND = new Random(3);

    [ProtoMember(1, AsReference=true)]
    public SomeData Data;

    // Prove that AnOwner is only instantiated once per deserialise
    public int i = RAND.Next(100);

    /// <summary>
    /// ProtoBuf deserialization constructor
    /// </summary>
    public AnOwner() { }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挖个坑埋了你 2024-12-08 05:09:42

基本上,它不是直接序列化 AnOwner,而是序列化一个假的(实际上并不存在)对象,该对象具有以下一项或多项功能:

  • 已存在的对象的现有键(整数
  • ) new key
  • 对象本身
  • 类型信息(如果启用 DynamicType)

序列化跟踪对象时,会检查内部列表;如果该对象存在(之前已经见过),则(仅)写入旧密钥。否则,将针对该对象生成并存储新密钥,并写入新密钥和对象。反序列化时,如果找到“新键”,则反序列化对象数据,并根据该键存储新对象(实际上这里的顺序有点复杂,要处理递归)。如果找到“旧密钥”,则使用内部列表来获取旧的旧对象。

对于几乎所有对象,比较都是在引用相等的基础上进行的(即使相等被覆盖)。请注意,这对于字符串的工作方式略有不同,字符串比较 字符串 相等性 - 因此字符串 "Fred" 的两个不同实例仍将共享一个密钥。

我相信大多数递归场景都受支持,但如果您遇到问题,请告诉我。

Basically, instead of serializing AnOwner directly, it serializes a fake (doesn't really exist) object with one or more of the following:

  • the existing key (integer) to an object that has already been seen
  • a new key
  • the object itself
  • the type information (if DynamicType is enabled)

When serializing a tracked object, an internal list is checked; if the object is there (has been seen before), then the old key (only) is written. Otherwise a new key is generated and stored against that object, and the new key and the object are written. When deserializing, if a "new key" is found, the object data is deserialized and the new object is stored against that key (actually the order here is a bit complex, to handle recursion). If an "old key" is found, the internal list is used to fetch the old old object.

For almost all objects, comparison is on a reference equality basis (even if equality is overriden). Note that this works slightly differently for strings, which are compared for string equality - so two different instances of the string "Fred" will still share a key.

I believe most recursion scenarios are supported, but if you get an issue please let me know.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文