2010-08-10 60 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

相關問題