为什么序列化时会出现“System.StackOverflowException未处理”异常?

时间:2010-08-10 21:12:36

标签: c# .net

我正在尝试序列化以下类:

[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 个答案:

答案 0 :(得分:2)

这将是你的问题:BindingNode : IEnumerable<BindingNode>这将是递归的,你会绑定StackOverFlowException。通常人们会创建两个类。

单班:

public class BindingNode
{
   /*..*/
}

集体类:

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

这种方法通常也会增加凝聚力并满足单一责任原则(SRP)。它是通过分开音乐会来实现的。集合逻辑放在集合类中,然后原始类执行它想要做的事情。

答案 1 :(得分:2)

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();
}

如果图形可以是循环的,那只是稍微复杂一点,你需要能够代替编写包含所有细节的元素,编写一个引用已经序列化到流的节点的元素,如否则实际的写作会永远持续下去,如果你很幸运,你很快会遇到堆栈溢出的不同原因(如果你运气不好,程序会先将文件的gigs和gigs首先写入磁盘,然后点击它)。

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。