C# 结构和属性更新问题

C# 结构和属性更新问题,c#,.net,properties,struct,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 {

我们使用一个结构来存储项目的数据,这个结构用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?
            }   
        }
    }
}
公共结构持续时间:IXmlSerializable { 私有常量字符串XML_VALUE=“VALUE”; 私有常量字符串XML_UNIT=“UNIT”; 公共浮点值{get;set;} 公共DurationUnit单元{get;set;}//DurationUnit是一个枚举 公共持续时间(浮点值,持续时间单位):此() { 价值=价值; 单位=单位; } 公共void ReadXml(XmlReader) { reader.MoveToContent(); Value=reader.GetAttribute(XML\u值); Unit=reader.GetAttribute(XML\u单位); //在debugger中,属性被正确初始化,我们使用XML中的值 } public void WriteXml(XmlWriter) { WriteAttributeString(XML_值,VALUE); WriteAttributeString(XML_单位,单位); //编写文件后,它在XML中包含正确的值 } } 公共类MyOtherClass():IXmlSerializable{ 公共持续时间SelectedDuration{get;set;} 公共管理类(){ SelectedDuration=新的持续时间(); } void IXmlSerializable.ReadXml(XmlReader) { //我们阅读xml,检查reader.Name,看看我们做了什么 if(当前节点为读取持续时间){ 使用(XmlReader subReader=reader.ReadSubtree()) { 选择duration.ReadXml(子读取器); //在这里,在SelectedDuration.ReadXml中输入了正确的值之后,我们得到了错误的(默认)值 //为什么?? } } } }
为什么??我们没有在方法的参数中给出持续时间?我知道“我们不应该有一个”可变结构“,但在这种情况下,我不明白什么会造成伤害(或者为什么?

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

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

两种可能的解决办法:

  • 让它成为一门课
  • 返回调用
    ReadXml()
    的结果,如下所示:

    SelectedDuration=SelectedDuration.ReadXml(子读取器)

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

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

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

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

    两种可能的解决办法:

  • 让它成为一门课
  • 返回调用
    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);
    }
    
    然后,编译器不会将属性复制到一个变量并调用该变量上的方法,而是会发出嘎嘎声,说明属性不能作为
    ref
    参数传递。然后,可以通过将调用代码重写为:

    var temp = SelectedDuration;
    Duration.ReadXml(ref SelectedDuration, subReader);
    SelectedDuration = temp;
    
    或者将
    SelectedDuration
    设置为字段而不是属性

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

    C#对结构做了一些坏事。代码的后面部分是eq等同于:

            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);
    }
    
    然后,编译器不会将属性复制到一个变量并调用该变量上的方法,而是会发出嘎嘎声,说明属性不能作为
    ref
    参数传递。然后,可以通过将调用代码重写为:

    var temp = SelectedDuration;
    Duration.ReadXml(ref SelectedDuration, subReader);
    SelectedDuration = temp;
    
    或者将
    SelectedDuration
    设置为字段而不是属性


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

    此代码不编译:
    公共类MyOtherClass()
    Matthew的答案是正确的,我只想补充一点,除非你知道你在做什么,否则不要使用结构。它们主要用于处理数学值(数字、向量、四元数等)和类似的事情,如颜色(基本上是由4个分量组成的向量)。此代码不编译: