2010-08-10 33 views
0

我试图序列化以下类:为什么序列化时出现“System.StackOverflowException was unhandled”异常?

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

作为一个方面说明,您不需要为XmlSerializer标记您的类Serializable。 – 2010-08-10 21:14:01

+0

此外,您应该使用“使用”块来清理流,而不仅仅是.Close。正如你所看到的,序列化代码可能会抛出可能使文件打开的句柄。 – McKay 2010-08-10 21:34:39

回答

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

如果图表可以通过循环,它只是稍微更复杂您需要能够代替编写包含所有详细信息的元素,以便编写一个引用已经序列化到流中的节点的元素,否则实际写作会永远持续下去,如果您幸运的话,您可以击中堆栈溢出的不同原因很快就会发生(如果你不幸的话,程序会先快速地将文件的演出和演出文件写入磁盘,然后点击它)。

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。

+0

噢,即使图是非循环的,如果节点可以有多个父节点,第二个选项可能会更好,因为仍然有可能连续两次对同一个节点进行序列化。虽然它不会造成循环,但是会造成浪费,并且它将作为两个不同的对象进行反序列化,并且会进一步浪费,失去身份特征。 – 2010-08-11 00:01:31

2

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

单类:

public class BindingNode 
{ 
    /*..*/ 
} 

的colleciton类:

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

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

+0

所以我想除了实现IEnumerable的非通用版本之外,没有其他解决方案? – Juan 2010-08-10 21:17:54

+0

想到的唯一的其他解决方案是创建两个类。一个用于'BindingNode',一个用于'BindingNodeCollection'。后者可以继承'IEnumerable '。 – 2010-08-10 21:19:48

+0

嗯,我这样做,直到我发现导致另一个问题:http:// stackoverflow。com/questions/3452376 /正确设置树的父节点-in -c – Juan 2010-08-10 21:39:15

相关问题