私有变量与财产?


41

在大多数情况下,在类内部为变量设置值时,我们会看到两种选择:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

是否有一个约定可以确定如何为类内部的变量赋值?例如,如果我在同一个类中有一个方法,我应该使用属性还是使用私有变量来分配它。我已经看过这两种方法,所以我想知道这是一个选择还是性能是一个因素(可能很小)。

Answers:


23

我将更进一步,将其归结为3个案例。尽管每个都有不同,但这是我在C#编程中大部分时间使用的规则。

在情况2和3中,请始终转到属性访问器(而不是字段变量)。在第一种情况下,您不必选择此选项。

1.)不可变属性(传递给构造函数,或在构造时创建)。在这种情况下,我使用具有只读属性的字段变量。我选择此方法而不是私有设置程序,因为私有设置程序不能保证不变性。

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.)POCO变量。可以在任何公共/私有范围内获取/设置的简单变量。在这种情况下,我只会使用自动属性。

public class Abc
{ 
  public int Foo {get; set;}
}

3.)ViewModel绑定属性。对于支持INotifyPropertyChanged的类,我认为您需要一个私有的后备字段变量。

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
MVVM示例为+1。实际上,这就是首先引发问题的原因。
爱德华

4
+1:将2/3与AOP混合,您将拥有使用INPC的绝妙方式。[通知] public int Foo {get; 组; }
史蒂文·埃弗里斯

1
@Job对于访问该类的任何类,私有的setter就足以实现不变性。但是,在类内部,私有设置程序不会阻止在初始构造后重复设置值。诸如“专用只读集”之类的语言功能在概念上可能会解决此问题,但是它不存在。
谢尔顿·沃肯汀

1
我是C#的新手,所以告诉我,为什么要使用public int Foo {get; set;}代替而不是public int Foo

1
如果类或结构将充当POCO或PODS,那么将字段包装在属性中有什么真正的优势?我完全理解,当类或结构需要(或可能将来需要)以保持其内容的不变性时(也许通过确保其他对象被更新以匹配它们),在属性中包装字段很有用。指定使用者可以以任何顺序写入任何值而没有限制或副作用,可以将哪些有用的行为添加到成员访问器中?
2012年

18

通常,我会说分配给构造函数中的字段并在其他地方使用该属性。这样,如果有人向该属性添加功能,您将不会在任何地方错过它。

当然,这不是性能因素。优化器将为您内联一个简单的get或set,最终的MSIL代码可能相同。


在构造函数中使用字段的任何特殊原因?较少出现奇怪副作用的机会?
签署

4
@Sign:我的猜测是,如果对该属性(现在或将来)进行验证,则您不希望冒在构造过程中验证失败的风险。在此阶段验证不合逻辑,因为在构造函数完成之前无法保证对象是稳定的。
史蒂文·埃弗斯

@Sign:你所说的和斯诺弗斯所说的一样。或者,如果我想记录对属性的更改,则可能不想记录初始设置。但是,我确实是说“一般”。
pdr 2012年

3
@Sign:问题是:如果可以在子类中重写该属性的set方法,则在创建对象或不一致的对象的过程中可能会产生副作用(即:重写的属性被编程为不设置任何值)该字段)。只有在set方法为私有方法或密封了类的情况下,在构造函数中使用属性才是安全的。
迭戈

4

要看。

首先,您应该尽可能使用自动属性:

public string MyValue {get;set;}

其次,更好的方法可能是使用属性,如果那里有任何逻辑,您可能应该自己传递它,尤其是如果该逻辑是线程同步。

但是,您还应该考虑到这可能会影响您的性能(一点点),如果您不正确地同步,则可能使自己陷入僵局,并且有时正确的方法是绕开属性中的逻辑。


3
我也喜欢public string MyValue {get; private set;}
Job

3

好吧,直截了当的方法就是将其分配给变量本身,因为无论如何,您都在类的方法内部,并且您可以控制类的行为。

但是关于属性的全部要点是它们将变量抽象掉了。就像您的示例中那样的简单属性仅对简单的公共成员变量完全没有用,而属性通常会(或应该)在其getter和setter内做其他事情。而且,如果您希望在更改类内部的属性时自动完成这些操作,那么在属性设置行为发生变化时不必更改每个变量分配就可以更轻松地使用属性而不是变量。

您只需要从概念上对此进行推理。该属性实际上是用于访问对象的某些内部状态的句柄,该状态可能由多个成员变量组成。因此,您必须问自己:是否只想更改底层内部状态(或仅更改其一部分),还是要整体更改表示该状态的抽象属性,而且大多数情况下确实是后者,因为您通常希望对象始终具有一致的状态。


2

如果这些属性的get / set实现有时会在以后更改(例如,您想在调用时引发一个事件set,或者稍后将在get函数中添加一些惰性评估机制),则可能是个好主意这样,您的类中的代码几乎会在所有情况下都使用该属性,除了(很可能极少见的)情况之外,在这种情况下,您明确希望使用这些事件或惰性评估机制。

无论如何,无论您做什么,都很有可能在以后以这种方式更改属性实现时,必须查看访问这些属性的类中的所有位置,以检查是否应真正访问该属性或应使用私有变量。


2

我总是使用公共财产。

通常,将在设置属性时应始终运行的某些逻辑添加到set属性的方法中,并设置私有字段,而由公共设置器将绕过那里的任何逻辑。

您对MVVM的评论引发了这个问题,我认为在使用MVVM时,这一点尤为重要。许多对象会PropertyChange向设置器发出通知,其他对象可以订阅此事件以在特定属性更改时执行某些操作。如果设置了私有变量,除非您也手动引发PropertyChanged事件,否则这些操作将永远不会执行。


+1是在大多数情况下(MVVM),PropertyChanged事件是必须的。那只能在财产内部被解雇。很好的解释。
爱德华

1

通常,由您决定在获取/设置属性时应如何处理属性及其后备字段。

通常,只是为了在代码之间保持一致,您应该在可用的公共访问器上使用公共访问器。这样您就可以以最少的代码更改来重构。如果需要将执行此设置的方法从类中删除,并放置在不再有后备字段的其他地方(例如基类),那么谁在乎呢?无论使用班级本身在哪里完成工作,您都在使用可用的东西。在大多数情况下,后备字段是实现细节。班上没有人不应该知道它的存在。

我可以想到的主要情况是,当访问器具有不想运行的其他逻辑(验证或更新类中的其他状态信息)时,应该使用支持字段而不是属性访问器。一个对象的初始填充就是一个例子。您可能有一个使用两个属性值来计算第三个属性值的类,该属性值也存储在备用字段中(出于持久性原因)。在初始化给定对象的新副本时,如果给定了来自DB的数据,则如果未设置其他所需的值,则每个都重新计算第三个值的属性访问器可能会抱怨。通过使用备用字段设置这两个(或三个)属性的初始值,可以绕过验证/计算逻辑,直到实例处于足够一致的状态以使逻辑正常工作。


0

始终使用有意义的选项。是的,我知道听起来很虚假,以至于无法回答。

属性的重点是提供一个接口,您可以通过该接口安全地访问数据模型。对于大多数情况,您始终希望通过该界面安全地访问数据模型,例如:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

但是在其他情况下,您可能只是使用属性作为数据模型的视图:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

如果使用的弧度形式有意义SomeAngle,则一定使用它。

最后,一定要喝自己的酷儿食品。您的面向公众的api应该具有足够的弹性以在内部工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.