一旦涉及对象引用,我似乎无法弄清楚如何正确使用CollectionDataContract
属性。非常感谢任何帮助。警告:代码示例可能看起来有点长,但我还没有找到一种更短的解释方法,所以请耐心等待。(更新:今天我知道了foobar是邪恶的。我纠正了,所以我用一个更有意义的例子取代了所有对"foo"one_answers"bar"的引用。)
考虑一个假想的测验应用程序的简单抽认卡界面:
interface IFlashcard
{
string Question {get; set;}
string Answer {get; set;}
}
由Flashcard
和FlippedFlashcard
类实现。FlippedFlashcard
的实例应该保持对Flashcard
相应实例的引用,以便反转Question
和Answer
,因为一些卡可能在"危险模式"下工作:
[DataContract]
class Flashcard : IFlashcard
{
[DataMember]
public string Question { get; set; }
[DataMember]
public string Answer { get; set; }
}
class FlippedFlashcard : IFlashcard
{
[DataMember]
public Flashcard OriginalFlashcard;
[DataMember]
public string Question
{
get { return OriginalFlashcard.Answer; }
set { OriginalFlashcard.Answer = value; }
}
[DataMember]
public string Answer
{
get { return OriginalFlashcard.Question; }
set { OriginalFlashcard.Question = value; }
}
}
让我们定义一个符合IFlashcard
的可序列化的对象集合…
[CollectionDataContract (ItemName = "Flashcard")]
class DeckOfFlashcards : List<IFlashcard>
{
}
…用一些抽认卡填满它:
Flashcard f1 = new Flashcard() { Question = "What do you get when you multiply 6 by 7?", Answer = "42" };
FlippedFlashcard f2 = new FlippedFlashcard { OriginalFlashcard = f1 };
DeckOfFlashcards myDeck = new DeckOfFlashcards();
myDeck.Add(f1);
myDeck.Add(f2);
f1.Question = "What is the answer to life, the universe and everything?";
Console.WriteLine(f2.Answer); // >> What is the answer to life, the universe and everything?
直到现在,一切都按照我想要的那样工作。当我将f1中的Question
从"将6乘以7得到什么?"改为"生命,宇宙和一切的答案是什么?"时,f2中的Answer
正确地反映了这一变化:
f1.Question = "What is the answer to life, the universe and everything?";
Console.WriteLine(f2.Answer); // >> What is the answer to life, the universe and everything?
我想序列化这个集合,并保持Flashcard
和FlippedFlashcard
实例之间的引用。下面是我的尝试:
List<Type> types = new List<Type> { typeof(Flashcard), typeof(FlippedFlashcard) };
DataContractSerializer dcs = new DataContractSerializer(typeof(DeckOfFlashcards), types, 100, false, true, null);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create("Flashcards.xml", settings))
dcs.WriteObject(writer, myDeck); // >> Question, Answer and OriginalFlashcard from f2 are serialized with "i:nil="true"
不幸的是,结果Xml文件将f2中的Question
, Answer
和OriginalFlashcard
列为空:
<?xml version="1.0" encoding="utf-8"?>
<DeckOfFlashcards xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Size="2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/Foobar">
<Flashcard z:Id="2" i:type="Flashcard">
<Answer z:Id="3">42</Answer>
<Question z:Id="4">What is the answer to life, the universe and everything?</Question>
</Flashcard>
<Flashcard z:Id="5" i:type="FlippedFlashcard">
<Answer z:Ref="4" i:nil="true" />
<OriginalFlashcard z:Ref="2" i:nil="true" />
<Question z:Ref="3" i:nil="true" />
</Flashcard>
</DeckOfFlashcards>
当我试图从文件中反序列化集合时…
DeckOfFlashcards myOtherDeck;
using (var reader = XmlReader.Create("Flashcards.xml"))
myOtherDeck = (DeckOfFlashcards)dcs.ReadObject(reader); // >> fires NullReferenceException at "set { OriginalFoo.Bar2 = value; }" with value "What is the answer to life, the universe and everything?"
…我得到一个NullReferenceException
,可能是当FlippedFlashcard
的Xml被击中。
有人知道吗?
明白了!
为了防止NullPointerException,有必要检查[DataContract]
和[DataMember]
属性。interface IFlashcard
完全不需要合约:
interface IFlashcard
{
string Question { get; set; }
string Answer { get; set; }
}
class Flashcard
的代码保持不变,就像我在描述中写的那样。唯一真正必要的更改是从class FlippedFlashcard
中的Question
和Answer
中删除 [DataMember]
属性,如下所示:
[DataContract]
class FlippedFlashcard : IFlashcard
{
[DataMember]
public Flashcard OriginalFlashcard { get; set; }
public string Question
{
get { return OriginalFlashcard.Answer; }
set { OriginalFlashcard.Answer = value; }
}
public string Answer
{
get { return OriginalFlashcard.Question; }
set { OriginalFlashcard.Question = value; }
}
}
现在一切正常。: -)