结构和属性更新问题

本文关键字:新问题 更新 属性 结构 | 更新日期: 2023-09-27 18:19:27

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

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

结构和属性更新问题

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

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

两种可能的解决方案:

  1. 改为将其作为类
  2. 将调用ReadXml()的结果分配回如下:

    SelectedDuration=SelectedDuration.ReadXml(子阅读器);

您也可以编写一个新的UpdateSelectedDurationFromXml()方法来代替:

public void UpdateSelectedDurationFromXml(XmlReader reader)
{
    Duration duration = new Duration();
    duration.ReadXml(reader);
    SelectedDuration = duration;
}

C#在结构上做了一些坏事。代码的后面部分相当于:

        using (XmlReader subReader = reader.ReadSubtree())
        {
            var temp = SelectedDuration;
            temp.ReadXml(subReader);
        }   

根据上面的代码,我认为SelectedDuration没有被ReadXML调用修改的原因非常清楚——因为该调用正在修改temp

因此,应该避免使用修改底层结构的结构实例方法,而是使用接受要修改的结构作为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;

或者通过使CCD_ 11成为字段而不是属性。

请注意,使用静态方法并将要修改的内容作为ref参数传递不会使代码工作,但它会防止编译器将不会编译的代码静默地更改为将编译但不可能工作的代码。