Почему при сериализации появляется исключение «System.StackOverflowException ishandled»? - PullRequest
0 голосов
/ 11 августа 2010

Я пытаюсь сериализовать следующий класс:

[Serializable()]
public class BindingNode : IEnumerable<BindingNode>
{
    public BindingNode()
    {
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public IEnumerator<BindingNode> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(BindingNode item)
    {
        throw new NotImplementedException();
    }
}

Первоначально это была коллекция ICollection вместо IEnumerable, но я удалила из своего кода столько, сколько могла, чтобы сохранить только то, что вызвало ошибку,Вот код, где происходит исключение:

    private void button1_Click(object sender, EventArgs e)
    {
        BindingNode node = new BindingNode();
        if (saveFileDialog1.ShowDialog() == DialogResult.OK)
        {
            Stream stream = File.Open(saveFileDialog1.FileName, FileMode.Create);
            XmlSerializer xmlFormatter = new XmlSerializer(node.GetType());
            xmlFormatter.Serialize(stream, node);
            stream.Close();
        }            
    }

Ответы [ 2 ]

2 голосов
/ 11 августа 2010

Поведение по умолчанию для XMLSerializer входит в цикл, поскольку в качестве части попытки выяснить, как сериализовать BindingNode, он затем пытается выяснить, как сериализовать IEnumerable<BindingNode>, и для этого пытается вычислить как сериализовать BindingNode.

Нельзя сказать, что вы не можете BindingNode реализовать IEnumerable<BindingNode>, просто поведение XMLSerializer по умолчанию не будет работать.

Если вы реализуете IXmlSerializable, вы сами контролируете сериализацию. Поскольку вы уже знаете структуру BindingNode, вам не нужно работать с ней во время выполнения! Если у вас есть гарантия ациклического графа (невозможно иметь BindingNode, который является его предком), то это тривиально:

public void WriteXml(XmlWriter writer)
{
    writer.WriteStartElement("BindingNode");
    //More stuff here.
    foreach(BindingNode contained in this)
        contained.WriteXml(writer);
    writer.WriteEndElement();
}

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

public int SomeSortOfUniqueID
{
    get
    {
        //guess what this property has to do!
    }
}
public void WriteXml(XmlWriter writer)
{
    WriteXml(writer, new HashSet<BindingNode>());
}
private void WriteXml(XmlWriter writer, HashSet<BindingNode> alreadyWritten)
{
    if(alreadyWritten.Add(this))
    {
        writer.WriteStartElement("BindingNode");
        writer.WriteAttributeString("uniqueID", SomeSortOfUniqueID.ToString());
        //More stuff here.
        foreach(BindingNode contained in this)
            contained.WriteXml(writer, alreadyWritten);
        writer.WriteEndElement();
    }
    else
    {
        //we need to reference a node already mentioned in the document.
        writer.WriteStartElement("BindingNode");
        writer.WriteAttributeString("refID", SomeSortOfUniqueID.ToString());
        writer.WriteEndElement();
    }
}

Конечно, вам также потребуется реализовать ReadXml () для повторного анализа XML.

2 голосов
/ 11 августа 2010

Это будет вашей проблемой: BindingNode : IEnumerable<BindingNode> Это будет рекурсивно, и вы получите StackOverFlowException.Обычно люди создают два класса.

Единый класс:

public class BindingNode
{
   /*..*/
}

Коллективный класс:

public class BindingNodeCollection : IEnumerable<BindingNode>
{
   /*..*/
}

Этот подход обычно также повышает сплоченность и удовлетворяет принципу единой ответственности(SRP).Это делается путем разделения концертов.Логика коллекции помещается в класс коллекции, а затем исходный класс выполняет то, что предполагалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...