C# 如何解决循环对象引用
我遇到了我认为可能是我的代码设计的一个主要问题,我希望这里有人可以向我解释如何解决这个问题。
我有两个类,每个类都有另一个类的属性,创建循环引用。我计划序列化这些类并使用 XSLT 格式化输出,但我假设这会由于循环引用而失败。
示例
public class Book
{
public BookShop TheShop = new BookShop();
}
public class BookShop
{
list<Book> Books = new list<Book>();
}
因此,在此示例中,每本书都将位于一个书店中,并且每个书店将有很多书。如果我序列化书店,它将序列化每本书,然后序列化书店,依此类推。我应该如何处理这个问题?
I've run into what i belive could be a major issue for my code design and i was hoping someone here could explain to me how i would work around the issue.
I have 2 classes which each have a property of the other class creating a circular reference. I plan on serializing these classes and using XSLT to format the output but i'm assuming this will fail due to the circular reference.
Example
public class Book
{
public BookShop TheShop = new BookShop();
}
public class BookShop
{
list<Book> Books = new list<Book>();
}
So from this example each book will be in a bookShop and each bookshop will have many books. If i serialize the bookshop it will then serialize each book which then serialize a bookshop and so on round and round. How should i handle this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
使用属性标记
TheShop
以防止其序列化。[XmlIgnore]
使用默认序列化器。http://www.codeproject.com/KB/XML/GameCatalog.aspx
可能只是您的示例存在问题,而不是您的真实代码:不要使用公共字段,而应使用属性。我认为
XmlSerializer
甚至不序列化公共字段。Tag
TheShop
with an attribute to prevent its serialization.[XmlIgnore]
with the default serializer.http://www.codeproject.com/KB/XML/GameCatalog.aspx
Probably just a problem with your example, not your real code: Don't use public fields but properties. I think
XmlSerializer
doesn't even serialize public fields.将
[XmlIgnore]
添加到TheShop
属性以防止其被序列化。然后您可以在反序列化时手动设置它。
Add
[XmlIgnore]
to theTheShop
property to prevent it from being serialized.You can then set it manually when deserializing.
最佳实践是让 BookShop 类实现一个接口 (IBookShop),然后让 Book 类存储该接口而不是具体类。您还应该将 BookShop 设为 Book 类中的一个属性:
Best practice would be to have the BookShop class implement an interface (IBookShop) and then have the Book class store the interface not the concrete class. You should also make BookShop into a property in the Book class:
如果要使用 System.Xml.Serialization.XmlSerializer,则应使用 System.Xml.Serialization.XmlIgnoreAttribute 装饰
TheShop
:是的,假设 BookShop 是您要序列化的根对象。 MSDN
If you're going to use
System.Xml.Serialization.XmlSerializer
, you should decorateTheShop
withSystem.Xml.Serialization.XmlIgnoreAttribute
:That is, assuming the
BookShop
is the root object you wish to serialize. MSDN首先您需要检查这是否确实是一个问题。如果您在拥有一本书时总是关心书店,并且您总是关心书店拥有的所有书籍,那么将整个图表序列化是非常明智的。这不会导致无限循环,因为序列化使用标识符来指示对已序列化的对象的引用(如果对类型中包含循环引用的图形进行 XML 序列化,则会出现错误) /strong>,但这是一个错误,而不是序列化 XML 问题所固有的,正如它可以解决的事实所证明的那样,请参阅 Why do序列化时出现“System.StackOverflowException 未处理”异常?)。
所以,也许你根本不想在这里做任何事,但你现在就很好。
否则,问题是 - 你到底想序列化什么?到目前为止,大多数建议都是不要序列化
TheShop
属性。这可能没问题,但如果您稍后需要访问该商店,它可能就没用了。如果您有每个商店的某种标识符(id 号、uri),那么您也许可以记住 - 访问
TheShop
首先查看私有_theShop
是否为 null,如果是,则根据该标识符将相关对象加载到_theShop
中。然后你只需要序列化标识符,而不是完整的对象。最后,如果您使用 XSLT 将输出格式化为其他规范(无论是用于显示的 XHTML,还是其他规范),您可能会发现更简单的是推出您自己的 XML 序列化。虽然这在很多方面都是一项更复杂的任务,但序列化生成的 XML 不太方便重新格式化显示这一事实可能意味着总体而言这种方式更简单。事实上,如果这是您序列化的唯一原因(您永远不会从生成的 XML 中反序列化),那么它可能会容易得多,因为您只需要考虑用于显示的 XML 需要什么,而不必担心其他任何事情。因此,序列化可能根本不是最好的方法,而只是一个
ToXml()
方法,或者另一个类中的WriteBookToXml()
方法。First you need to check whether this is really a problem. If you always care about a bookshop when you have a book, and you always care about all the books a bookshop has, then it's perfectly sensible to have the whole graph serialised. This doesn't result in an infinite loop, because the serialisation uses an identifier to indicate a reference to an object already serialised (there is a bug if you do an XML serialisation of a graph with a circular reference in its types, but that's a bug rather than inherent to the problem of serialising XML, as the fact that it can be resolved proves, see Why do I get a "System.StackOverflowException was unhandled " exception when serializing? on that).
So, maybe you don't want to do anything here at all, and you're fine as you are.
Otherwise, the question is - just what do you want to serialise? Most suggestions so far have been to not serialise the
TheShop
property. This could be fine, or it may be useless if you will need to later access that shop.If you have some sort of identifier (id number, uri) for each shop, then you could perhaps memoise - access to
TheShop
looks first at whether a private_theShop
is null, and if it is, loads the relevant object into_theShop
based on that identifier. Then you just need to serialise the identifier, not the full object.Finally, if you are using XSLT to format the output to some other specification (whether XHTML for display, or something else) you may find it simpler just to roll your own XML serialisation. While this is a more complicated task in many ways, the fact that the XML produced by serialisation isn't particularly convenient for reformatting for display may mean that overall it's simpler this way. Indeed, if this is your only reason for serialising (you will never deserialise from the XML produced) then it may be much easier, as you need only consider what the XML for display needs, and not worry about anything else. Hence serialising may not be the best approach at all, but simply a
ToXml()
method, or aWriteBookToXml()
method in another class.