【问题标题】:Difference between member variable and member property?成员变量和成员属性的区别?
【发布时间】:2011-01-17 23:44:00
【问题描述】:

在某些情况下,我在类的顶部声明了成员变量,然后还声明了一个属性来访问或设置该成员变量,但我问自己该属性是否是必要的,如果它只是要被访问的变量并且从类内部设置,没有其他地方,那么使用属性访问和设置成员变量而不是直接对成员变量本身进行设置有什么好处。这是一个例子:

public class Car
{

    int speed; //Is this sufficient enough if Car will only set and get it.

    public Car(int initialSpeed)
    {
        speed = initialSpeed;
    }

    //Is this actually necessary, is it only for setting and getting the member
        //variable or does it add some benefit to it, such as caching and if so,
        //how does caching work with properties.
    public int Speed 
    {
        get{return speed;}
        set{speed = value;}
    }

        //Which is better?
        public void MultiplySpeed(int multiply)
        {
            speed = speed * multiply; //Line 1
            this.Speed = this.Speed * multiply; //Line 2

            //Change speed value many times
            speed = speed + speed + speed;
            speed = speed * speed;
            speed = speed / 3;
            speed = speed - 4;

        }
}

在上面,如果我没有属性 Speed 来设置和获取变速,并且我决定将 int speed 更改为 int spd,那么我将不得不在任何使用它的地方将 speed 更改为 spd,但是,如果我使用诸如 Speed 之类的属性来设置和获取速度,我只需在属性的获取和设置中将 speed 更改为 spd,因此在我的 MutilplySpeed 方法中,类似于 this.Speed = this.Speed + this .Speed + this.Speed 不会中断。

【问题讨论】:

标签: c# oop


【解决方案1】:

如果变量是 private ,我通常不会为它创建属性。如果它以任何方式暴露在类型之外,出于不同的原因,我总是通过属性暴露它:

  • 今天可能没有必要,但如果以后有必要,这将是一个重大更改
  • 数据绑定仅适用于属性,不适用于字段(我认为,不是大数据绑定用户)
  • 它允许在访问值时插入验证、日志记录、断点

另外,如果该字段通过属性公开,我总是通过该属性访问它,即使在类中也是如此。

更新
为了响应您更新的代码示例:这里的代码设计有很多事情需要考虑。

  • 可读性与速度
  • “原子性”
  • 其他副作用

一个典型的建议(我觉得非常好)是“写清楚,测试性能”。这意味着当您编写代码时,您首先关心的应该是查看代码时是否清楚代码的作用。这通常(但不总是)比代码的原始速度更重要。当您确定获得它的位置后,写入速度优化。访问属性会比直接读取字段慢一点,但在大多数情况下,差异可以忽略不计(如果可以测量的话)。

原子性可能是个问题。给定您的代码示例,我们有字段 speed,它通过属性 Speed 公开。如果方法MultiplySpeed 需要对值执行多次更新,则这些中间值将在计算进行期间的不同时间通过Speed 属性提供。无论您是直接更新字段还是通过属性更新字段,这都是正确的。在这种情况下,最好先将值放入局部变量中,然后将其用于计算,并在完成后将该变量的值分配回属性。

最后,其他副作用。更改Speed 的值可能会引发一个事件(例如SpeedChanged)。在这种情况下,最好不要在计算完成之前进行更新。

我喜欢将属性视为合同,将字段视为实现。任何需要该值的人(除了我的核心类型)都应该使用合约。只有在有充分理由绕过合约的情况下才应该依赖实现。

是的,如果您将对字段的访问封装在属性中,自然更改字段名称将需要更少的更新(并且可能字段名称也变得不那么重要了)。

我希望这是有道理的,并且不是太离题;)

【讨论】:

  • 属性也可以通过反射访问/设置
  • 我同意在类中使用该属性,即使在构造函数中也是如此。很高兴知道您的验证逻辑总是被调用。
  • +1 甚至可以从内部通过属性使用字段,因为它避免了令人讨厌的“setter-doesnt-run-when-it-it-should”错误。
  • @Rob:字段也可以通过反射访问/设置(Type.GetFieldsFieldInfo.GetValueFieldInfo.SetValue
  • 感谢您的信息。我有个问题。关于您的第一个要点:您是说如果我将变速更改为 spd,我将不得不遍历所有代码并将我使用的任何地方更改为变速并将其更改为 spd,但是如果我使用诸如速度,我只需要在get和set中将速度更改为spd?我理解正确吗?
【解决方案2】:

在我看来,语言设计被破坏了。不应该有两种做事的方式有如此多的语义重叠。属性/字段应该根据它们的使用方式无缝地提供任何一种方法的好处。如果程序对属性特性的使用最少,它们的行为方式应该与字段相同。此外,不需要声明空 get;并设置;这种情况下的方法。我觉得这些差异是人为的。

这是一门很棒的语言;大部分都很干净。这并不意味着它不应该在“下次”时改进。

【讨论】:

    【解决方案3】:

    事实是,如果没有额外的逻辑,公开声明的字段和具有私有后备存储的公共属性之间没有太大区别。尽管如此,使用属性仍然被认为是最佳实践。

    在大家对可扩展性提出质疑之前,请记住,如果您以后确实需要添加功能,您可以将名称与属性一起保留,并为后备存储引入一个新名称,这样就不会发生重大变化。

    【讨论】:

      【解决方案4】:

      我同意Frederik's 的回答。使遵循他的建议的工作量稍微减少的一件事是使用自动属性。这些只是自动生成标准 getter/setter 逻辑的属性。您没有得到任何验证,但您以后可以随时将自动属性替换为标准属性。此替换不是重大更改。

      在这里,我已将示例中的 Speed 属性替换为自动属性。请注意,成员变量消失了,您的类必须通过属性访问它。

      public class Car
      {
          public Car(int initialSpeed)
          {
              Speed = initialSpeed;
          }
      
          public int Speed { get; set; }
      
          public void MultiplySpeed(int multiply)
          {
              Speed *= multiply;
          }
      }
      

      您还可以使用另一种称为“使用私有集获取”的风格。这意味着 getter 是公共的,但 setter 是私有的。你这样定义它:

          public int Speed { get; private set; }
      

      至于您关于this. 前缀的问题,通常无关紧要。唯一重要的是您定义了与成员变量同名的方法参数或局部变量。然后就可以使用this来访问成员变量了。

      【讨论】:

        【解决方案5】:

        你忘了提一件事,属性会在你扩展你的类时帮助你。 如果您的类设计正确,则基类中的变量应为private。没有实际属性public 属性。您将无法从扩展类中访问这些私有变量。我们正在谈论公共与私人,我不包括受保护是有原因的:)。

        只是一些值得一提的注意事项:

        • 扩展类时的属性辅助
        • 我的属性使代码更具可读性(此外还有 this.privateVariable 版本 PublicPropertyVariableName)
        • 属性可以确保只读、私有集、公共获取等(对其他程序员来说更具可读性)。考虑一个 IDentifier 需要公共获取但私有集的情况
        • 就我个人而言,太多的 get/set 似乎会使代码复杂化,降低代码的可读性,过多的额外不必要的语法
        • 继承/扩展到扩展类不允许您继承私有变量,答案就是属性。 (这里再次没有提到受保护,那是另一回事)
        • 对我来说,即使类有私有变量,我的类方法仍然使用该属性来访问或使用该私有变量
        • 不要忘记验证,它使验证变得更加容易,尤其是在可读性方面。

        这些只是一些常见的东西(我的 2 美分虽然大部分都是)。

        【讨论】:

        • 所以我从您的回答中得知,通过将变量设为私有并使用属性来设置和获取它们,我正在利用封装?
        • 您也可以使用受保护的变量来做到这一点。
        【解决方案6】:

        我知道的唯一真正原因是是否从程序集外部访问该字段。在这种情况下,如果您想向该字段添加轻量级功能(可能设置 Dirty 标志或验证更改),您必须将其更改为更改调用程序集查看它的方式的属性,这也需要重建.在极少数情况下,您可能会发现您无法控制该程序集,那么您就有问题了。

        不要让 OO 狂热者告诉您使用公共字段在哲学上是错误的,尽管我可能同意自动属性使这个论点变得毫无意义。

        【讨论】:

        • “不要让 OO 狂热者告诉你使用公共字段在哲学上是错误的,尽管我可能同意自动属性使这个论点变得毫无意义。”我不是 OO 狂热者,但这是不好的做法。所有成员变量都应该是私有的。
        • 为什么,乔恩?别误会,我以前也是这么想的;老实说,我仍然出于习惯这样做。但在解释原因时遇到了挑战,我没有比刚才给出的更好的答案了。
        • @JonH - 做出诸如“所有成员变量都应该是私有的”这样的笼统声明对于在不同条件下工作并具有不同目标的人来说并不公平。我发现性能下降令人无法忍受 - 但也许我应该使用 C 和结构来代替?从你的 POV 来看,情况应该更糟。
        • @pdr,你不可能通过使用“狂热者”这样的标签来描述像 JonH 这样的人来帮助任何人 :-) 我可以抛出几个带有(不公平)负面含义的词来说“他的背景和我的不一样”。
        • 只有当我想要公开一个不会更改且会经常使用的(通常是静态的)值时,我才使用公共字段。想想 EventArgs.Empty 和 String.Empty。值得记住的是,除非您将克隆逻辑放在 getter 中,否则属性无法与 .Net 的“对象派生”世界中的封装相提并论。它们的使用受制于与 c++ 中的 setter 和 getter 不同的原则。从根本上说,它们在反射时提供了一个单独的范围,启用了诸如绑定额外逻辑(如 INotifyPropertyChanged)之类的功能,并让您控制访问方向。
        【解决方案7】:

        它不添加缓存,但确实允许一致的接口。

        想象一下你以后必须通过添加一个常数来修改速度。在属性允许这种操作的情况下,使用成员变量会很困难。

        同样在类内部,您应该再次访问该属性以保持一致性(想象上面的场景,您有一个直接访问成员变量的类)。

        【讨论】:

          【解决方案8】:

          验证之类的事情可以在一个地方进行。该成员是封装的,您需要担心其他类的验证和其他事情。

          在您当前的情况下,它并没有真正产生影响,但是当您需要更改变量或需要添加行为时,使用属性会更容易,因为您只有一个地方需要更改它。

          【讨论】:

            猜你喜欢
            • 2021-10-22
            • 2021-11-03
            • 1970-01-01
            • 1970-01-01
            • 2011-11-10
            • 2018-12-23
            • 2011-05-15
            • 1970-01-01
            相关资源
            最近更新 更多