Исключение рекурсии - PullRequest
       2

Исключение рекурсии

3 голосов
/ 19 февраля 2012

У меня проблема с рекурсией в этом коде:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using ProtoBuf;

namespace ConsoleApplication4
{
[Serializable]
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class GeneralWrapper<T> where T : new()
{
    public GeneralWrapper()
    {
        Datas = new T();
    }

    public T Datas { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        List<Document> _documents = new List<Document>();

        for (int i = 0; i < 100000; i++)
        {
            Document _document = new Document()
            {
                DocumentName = "asdadsf"
            };

            _documents.Add(_document);
        }

        BinaryFormatter _formatter = new BinaryFormatter();

        FileStream fs = new FileStream
            ("Person1.bin", FileMode.OpenOrCreate);

        ProtoBuf.Serializer.Serialize(fs, _documents);

        fs.Close();

        // Deserialize.
        fs = new FileStream
            ("Person1.bin", FileMode.OpenOrCreate);

        List<Document> _document22 = ProtoBuf.Serializer.Deserialize<List<Document>>(fs);

        fs.Close();
    }
}

[ProtoContract]
public class Document
{
    public Document()
    {
        _count = 234234924;

        Section = new Section();

        Section.SectionName = "sdfasd";
    }

    [ProtoMember(1)]
    public string DocumentName { get; set; }

    [ProtoMember(2)]
    Dictionary<string, List<string>> Hello { get; set; }
    [ProtoMember(3, AsReference=true)]
    public Section Section { get; set; }

    [ProtoMember(4)]
    private string _sectionName;
    [ProtoMember(5)]
    public string SectionName
    {
        get
        {
            return Section.SectionName;
        }

        set
        {
            _sectionName = value;

            Section.SectionName = _sectionName;
        }
    }

    public int _count;
    public int Count
    {
        get
        {
            return _count;
        }
    }
}

[Serializable]
[ProtoContract]
public class Section
{
    public Section()
    {
        Section1 = new SectionInner(this);

        Hellos = new List<GeneralWrapper<List<string>>>();

        GeneralWrapper<List<string>> _hello = new GeneralWrapper<List<string>>();

        _hello.Datas.Add("hello");

        Hellos.Add(_hello);

        DHello = new Dictionary<string, List<Section>>();

        if (!DHello.ContainsKey("asdf"))
        {
            List<Section> _dhello1 = new List<Section>();

            _dhello1.Add(this);

            DHello.Add("asdf", _dhello1);
        }
    }

    [ProtoMember(1, AsReference=true)]
    public SectionInner Section1 { get; set; }
    [ProtoMember(2)]
    public string SectionName { get; set; }

    [ProtoMember(3, AsReference=true)]
    public Dictionary<string, List<Section>> DHello { get; set; }

    List<GeneralWrapper<List<string>>> Hellos { get; set; }
}

[Serializable]
[ProtoContract]
public class SectionInner
{
    public SectionInner(Section section)
    {
        Section = section;
    }

    [ProtoMember(1, AsReference=true)]
    public Section Section { get; set; }
}

}

Я, очевидно, сделал код очень рекурсивным для начала, потому что это то же самое, что и мой настоящий проект. Проблема, кажется, заключается в следующем:

 Dictionary<string, List<Section>>

Когда в этот словарь ничего не добавляется, все сериализуется нормально. Если список добавляется в словарь с определенным ключом, происходит рекурсия.

Поддерживается ли этот код / ​​синтаксис в protobuf-net?:

 Dictionary<string, List<Section>>

Нужно ли помещать список во внешний класс-оболочку, например:

 Dictionary<string, Wrapper<List<Section>>>

Спасибо за вашу помощь. Я новичок в protobuf-net.

1 Ответ

2 голосов
/ 19 февраля 2012

Во-первых, я должен отметить, что выполнение такого большого количества настроек в конструкторах не является хорошей идеей.Если это указывает на ваш реальный код, вы можете пропустить конструкторы во время десериализации ([ProtoContract(SkipConstructor=true)]).Если это просто иллюстративно, хорошо.

Да, такие вещи, как словари и списки, поддерживаются, однако * непосредственно вложенных списков - нет, так что List<List<...>> на данный момент не в порядке.Вы, вероятно, сойдете с этим с Dictionary<TKey,List<...>>, потому что его пара ключ-значение уже действует как обертка посередине.

Повторная рекурсия: protobuf-net имеет поддержку многих сценариев рекурсии, ноэто как расширение к формальной спецификации.Таким образом, вам нужно явно включить его, и обратите внимание: этот сценарий будет нелегко взаимодействовать, поскольку для этого нет формальной спецификации;но: [ProtoMember(n, AsReference=true)] включает отслеживание объекта для отдельного члена.Обратите внимание, что все места, которые используют этот объект, должны быть помечены как таковые, иначе они будут использовать сериализацию дерева.

Re "почему рекурсия не поддерживается напрямую" - потому что: protobuf (формальная спецификация) ведет себя как большинство сериализаторовздесь и дерево сериализатор.Примечание: такие вещи, как XmlSerializer, JavascriptSerializer и DataContractSerializer (в режиме по умолчанию) являются и древовидными сериализаторами и могут взорваться, если получить рекурсивную структуру.Это нормально.Protobuf-net делает все возможное, чтобы разрешить это в нескольких сценариях, но не может включить его по умолчанию, поскольку для этого потребуется другая структура данных, что противоречит цели кросс-платформенной спецификации данных.

Если я пропустил любой из ваших вопросов, пожалуйста, скажите.

...