结构和属性更新问题

时间:2014-08-25 08:14:20

标签: c# .net properties struct

我们使用结构来存储项目的数据,这个结构在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?
            }   
        }
    }
}

为什么呢?我们没有给出方法中的持续时间参数?我知道“我们不应该有一个”可变的结构“,但在这种情况下,我不明白什么会伤害(或为什么?)

2 个答案:

答案 0 :(得分:4)

因为Duration是结构,所以它是值类型

因此public Duration SelectedDuration { get; set; }中定义的getter将返回结构的COPY,并且您对其所做的任何更改都将发送给副本,而不是原始副本。

两种可能的解决方案:

  1. 改为上课。
  2. 像这样分配调用ReadXml()的结果:

    SelectedDuration = SelectedDuration.ReadXml(subReader);

  3. 您也可以编写一个新的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参数传递将不会使代码工作,但它会阻止编译器静默更改无法编译为代码的代码将编译,但不可能工作。