Pourquoi ai-je une exception «System.StackOverflowException non gérée» lors de la sérialisation?

J’essaie de sérialiser la classe suivante:

[Serializable()] public class BindingNode : IEnumerable { public BindingNode() { } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } public IEnumerator GetEnumerator() { throw new NotImplementedException(); } public void Add(BindingNode item) { throw new NotImplementedException(); } } 

C’était à l’origine une ICollection au lieu d’un IEnumerable, mais j’ai supprimé autant que possible de mon code pour ne conserver que la cause de l’erreur. Voici le code où l’exception se produit:

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

Le comportement par défaut de XMLSerializer se heurte à une boucle car, dans le cadre de la procédure de sérialisation d’un BindingNode , il essaie ensuite de comprendre comment sérialiser un IEnumerable , et de le faire. un BindingNode .

Rien ne dit que vous ne pouvez pas demander à BindingNode implémenter IEnumerable , mais simplement que le comportement par défaut de XMLSerializer ne fonctionnera pas.

Si vous implémentez IXmlSerializable, vous contrôlez vous-même la sérialisation. Comme vous connaissez déjà la structure d’un BindingNode, vous n’avez pas à vous en soucier au moment de l’exécution! Si vous avez la garantie d’un graphe acyclique (il est impossible d’avoir un BindingNode qui soit un ancêtre de lui-même), alors c’est sortingvial:

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

Si le graphe peut être cyclique, c’est un peu plus compliqué dans la mesure où vous devez être capable d’écrire un élément avec tous les détails inclus, d’écrire un élément qui fait référence à un noeud déjà sérialisé dans le stream, sinon l’écriture dure toujours et si vous avez de la chance, vous rencontrez très vite une cause différente de débordement de stack (si vous avez de la malchance, le programme écrit gaiement les concerts d’un disque sur un disque, puis le frappe).

 public int SomeSortOfUniqueID { get { //guess what this property has to do! } } public void WriteXml(XmlWriter writer) { WriteXml(writer, new HashSet()); } private void WriteXml(XmlWriter writer, HashSet alreadyWritten) { if(alreadyWritten.Add(this)) { writer.WriteStartElement("BindingNode"); writer.WriteAtsortingbuteSsortingng("uniqueID", SomeSortOfUniqueID.ToSsortingng()); //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.WriteAtsortingbuteSsortingng("refID", SomeSortOfUniqueID.ToSsortingng()); writer.WriteEndElement(); } } 

Bien sûr, vous devrez également implémenter ReadXml () pour ré-parsingr le code XML.

Ce serait votre problème: BindingNode : IEnumerable Ce serait récursif et vous provoqueriez une StackOverFlowException . Généralement, les gens créent deux classes.

La classe unique:

 public class BindingNode { /*..*/ } 

La classe de collection:

 public class BindingNodeCollection : IEnumerable { /*..*/ } 

Cette approche augmente généralement la cohésion et satisfait au principe de responsabilité unique (SRP). Il le fait en séparant les concerts. La logique de collection est placée dans la classe de collection, puis la classe d’origine fait ce qu’elle était censée faire.