我们使用结构来存储项目的数据,这个结构在Xml中序列化然后读取。问题是,当我们在结构的Read方法中时,我们将正确的值放在属性中,但是当我们在拥有该结构的类中时,我们仍然具有默认值。
public struct Duration : IXmlSerializable
{
private const string XML_VALUE = "Value";
private const string XML_UNIT = "Unit";
public float Value { get; set; }
public DurationUnit Unit { get; set; }//DurationUnit is an enum
public Duration(float value, DurationUnit unit): this()
{
Value = value;
Unit = unit;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
Value = reader.GetAttribute<float>(XML_VALUE);
Unit = reader.GetAttribute<DurationUnit>(XML_UNIT);
//Here in debugger, the properties are correctly initialized we the value in the XML
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString(XML_VALUE, Value);
writer.WriteAttributeString(XML_UNIT, Unit);
//After we wrote the file, it contains the correct values in the XML
}
}
public class MyOtherClass():IXmlSerializable{
public Duration SelectedDuration { get; set; }
public MyOtherClass(){
SelectedDuration = new Duration();
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
//we read the xml, we check the reader.Name to see what we do
if(the_current_node_is_the_duration_to_read){
using (XmlReader subReader = reader.ReadSubtree())
{
SelectedDuration.ReadXml(subReader);
//And here, after we were having the correct values into the SelectedDuration.ReadXml, we have the wrong(default) values
//Why?
}
}
}
}
为什么呢?我们没有给出方法中的持续时间参数?我知道“我们不应该有一个”可变的结构“,但在这种情况下,我不明白什么会伤害(或为什么?)
答案 0 :(得分:4)
因为Duration
是结构,所以它是值类型。
因此public Duration SelectedDuration { get; set; }
中定义的getter将返回结构的COPY,并且您对其所做的任何更改都将发送给副本,而不是原始副本。
两种可能的解决方案:
像这样分配调用ReadXml()
的结果:
SelectedDuration = SelectedDuration.ReadXml(subReader);
您也可以编写一个新的UpdateSelectedDurationFromXml()
方法来代替:
public void UpdateSelectedDurationFromXml(XmlReader reader)
{
Duration duration = new Duration();
duration.ReadXml(reader);
SelectedDuration = duration;
}
答案 1 :(得分:0)
C#用结构做了一些邪恶的事情。代码的后半部分等同于:
using (XmlReader subReader = reader.ReadSubtree())
{
var temp = SelectedDuration;
temp.ReadXml(subReader);
}
我认为很明显,鉴于上述代码,SelectedDuration
调用未对ReadXML
进行修改,因为该调用正在修改temp
。
因此,应该避免使用修改底层结构的struct实例方法,而是使用接受将结构修改为ref
参数的静态方法。如果ReadXml
方法写为:
public static void ReadXml(ref Duration it, XmlReader reader)
{
reader.MoveToContent();
it.Value = reader.GetAttribute<float>(XML_VALUE);
it.Unit = reader.GetAttribute<DurationUnit>(XML_UNIT);
}
并且电话被写为
Duration.ReadXml(ref SelectedDuration, subReader);
然后,不是将属性复制到变量并在其上调用方法,编译器会强调不能将属性作为ref
参数传递。然后可以通过将调用代码重写为:来解决此问题
var temp = SelectedDuration;
Duration.ReadXml(ref SelectedDuration, subReader);
SelectedDuration = temp;
或者通过使SelectedDuration
成为字段而不是属性。
请注意,使用静态方法并将要修改的事物作为ref
参数传递将不会使代码工作,但它会阻止编译器静默更改无法编译为代码的代码将编译,但不可能工作。