【问题标题】:What happens when you make assignments to fields or properties of properties对属性的字段或属性进行分配时会发生什么
【发布时间】:2008-11-20 16:28:24
【问题描述】:

假设您有这样的属性:

Person person1;


public Person Captin{
    get{
        return person1;
    }
    set{
        person1 = value;
    }
}

public void SomeFunction(){
    Captin.name = "Hook"
}

在这种情况下,如果您在属性上设置名称,我们知道 Hook 的新名称将应用于 person1 的基础值。如果我们的实现有点不同怎么办:

public Person Captin{
    get{
        return ReadCaptinFromDisk();
    }
    set{
        WriteCaptinToDisk(value);
    }
}

public void SomeFunction(){
    Captin.name = "Hook"
}

在这种情况下,为了正确设置基础值,我们需要调用 Captin 的设置代码作为对 Captin.name 的赋值的一部分。

我很想知道参数集代码是否会在属性引用上调用字段分配或方法调用集。特别是对于这种需要将值传播到磁盘(等)的情况。

【问题讨论】:

  • 我们知道 Hook 的新名称将应用于 person1 的底层值 那么,如果 Person 类型是一个 ,然后 getter 将返回一个 referencePerson object。然后该对象将被突变。私有字段person1 是对同一对象的另一个引用。但是如果Person 是一个struct,那么getter 将返回一个value,它是person1 的值的copy。因此,如果 Person 是值类型,即使您的第一个代码也无法工作。

标签: c# properties get set


【解决方案1】:

每次访问 Captin 属性时,它都会从磁盘读取。但是,如果您更改属性“名称”,它将不会写入磁盘。如果您执行类似的操作,它只会写入磁盘

public void SomeFunction() {
   Person p = Captin;
   p.name = "Hook";
   Captin = p;
}

【讨论】:

    【解决方案2】:

    正如@Joe 所说,它不会写入磁盘。我只是想补充一点,这是因为您只使用了 getter,而不是 setter。 @Joe 的示例同时使用两者。

    在我看来,这既是对 getter 的非常糟糕的使用,又违反了关注点分离。您应该有一个数据层来处理持久化数据。这个逻辑不应该在你的业务对象中。

    【讨论】:

    • 我最感兴趣的是使用对象的缓存副本
    【解决方案3】:

    该属性的设置器只有在有人实际直接分配给它时才会被调用。

    至于你的代码好不好:这是文档的问题。

    当你有一个返回可变值的属性时,你应该指出它是否会发生什么突变。您是返回“真实”数据的副本,还是返回真实数据本身?当一个属性(或普通方法)返回某种集合时,这经常出现——它是可变的?如果我改变它会发生什么?

    如果您记录您的属性​​,说明返回的数据只是一个副本,并且更改不会反映在任何地方,那很好。如果你让它模棱两可,那你就会遇到问题。

    不变性当然消除了这些顾虑...

    【讨论】:

      【解决方案4】:

      一个类类型的变量、参数、字段、返回值或其他这样的存储位置应该被认为是持有一个“对象ID”。如果某个对象Foo 有一个名为Bar 的属性,该属性由字段_Bar 支持,并且_Bar 包含“对象id#24601”,那么语句Foo.Bar.Text = "George" 将调用Text对象 #24601 上的设置器,其值为“George”。请注意,此语句不会修改对象Foo 本身(其字段_Bar 在语句执行之前将保留“object id#24601”,并且在执行后仍将保留它);但是,它可能会影响对象 #24601。

      结构类型的存储位置应被视为保存其所有字段(公共和私有)的内容。如果Foo.BozRectangle 类型的属性和支持字段_Boz,则对Foo.Boz 的访问将创建一个Rectangle 类型的新临时实例,其所有字段都将被复制来自Foo._Boz 的那些。尝试读取Foo.Boz.X 会将_Boz 的所有字段复制到一个临时实例,然后访问该实例的字段X

      请注意,一些非常古老而邪恶的 C# 编译器会将 Foo.Boz.X = 5; 之类的代码解释为 Rectangle temp; temp.X = 5;,丢弃 temp 的结果值但不发出任何警告。这样的编译器行为导致一些人声明结构应该是“不可变的”,以确保这样的代码会产生编译器错误而不是产生虚假行为。不幸的是,尽管X 是一个可变字段,任何体面的编译器都会禁止此类代码,但这种信念一直持续到今天。

      请注意,更新可变结构类型的属性的正确惯用方法是:

      矩形温度 = MyListOFRectangles[5]; 温度 X = 5; MyListOFRectangles[5] = temp;

      如果已知Rectangle 有一个名为X 的公共整数字段,而MyListOfRectanglesList<Rectangle>,则无需了解Rectangle 的任何其他属性,构造函数等知道上面的代码会改变MyListOfRectangles[5].X,但不会影响MyListOfRectangles[5]的任何其他属性,也不会影响MyListOfRectangles[4]的任何属性。很好,清晰,简单。与任何其他类型的数据类型不同,暴露字段结构允许以清晰一致的方式对值进行分段编辑。

      【讨论】:

      • 赞成。所以你认为编写可变结构是可以的(假设编译器会知道它不能改变方法调用的直接结果,如GetSomeStructValue().Name = "Hook"; 不会编译)?
      猜你喜欢
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 2014-11-13
      • 1970-01-01
      • 2016-04-05
      • 2012-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多