公共字段与自动属性


353

人们经常被告知,我们应该通过为类字段创建getter和setter方法(C#中的属性)来保护封装,而不是将字段暴露给外界。

但是在很多情况下,只有一个字段可以保存一个值,而无需获取或设置任何计算。对于这些,我们都将使用以下数字:

public class Book
{
    private string _title;

    public string Title
    {
          get{ return _title;  }
          set{ _title = value; }
    }
}

好吧,我有一个坦白的话,我忍不住写所有的东西(真的,不必写它,而不必去看它),所以我流氓并使用了公共领域。

然后是C#3.0,我看到它们添加了自动属性:

public class Book
{
    public string Title {get; set;} 
}

哪个比较整洁,我对此很感激,但实际上,与仅公开场地有何不同?

public class Book
{
    public string Title;
}


6
我已将场地转换为财产,以便可以在设置器上设置断点
Ian

1
我倾向于将任何非私有财产都做为财产,因为一路走来,我必须将一个字段重构为财产,这导致了一些不必要的麻烦。 属性,字段和方法。天啊!呼唤过去一直困扰我的不兼容性。
史蒂芬·韦克斯勒

2
prop代码段可快速创建属性。只需键入prop然后制表符。
Tono Nam

Answers:


175

在我前段时间遇到的一个相关问题中,有一个指向Jeff博客上的帖子的链接,解释了一些区别。

属性与公共变量

  • 反射在变量和属性上的工作方式不同,因此,如果您依赖反射,则使用所有属性会更容易。
  • 您无法与变量进行数据绑定。
  • 将变量更改为属性是一项重大更改。例如:

    TryGetTitle(out book.Title); // requires a variable

24
“将变量更改为属性是一项重大变化。” 当然,这仅适用于编写可重用的库,而大多数开发人员都没有这样做。
史蒂文

30
此外,属性(甚至是自动属性)也可以是虚拟的,而字段则不能。因此,基类可以具有由编译器为auto-prop生成的简单后备域实现,而派生类可以执行其他验证或其他逻辑/计算。
KeithS

28
同样,字段是变量,可以通过引用(refout关键字)传递,而属性是一对访问器,不能通过引用传递。例如bool success = TryGetMyTitle(out myBook.Title);,使用out将与字段一起使用而不与属性一起使用。这是一个清楚的示例,说明了为什么从字段更改为属性是一项重大更改!
Jeppe Stig Nielsen

2
@KyleBaran不,这没有多大意义,因为属性是一对访问器方法,而不是变量。通常要做的是声明一个局部变量(可能读取该属性并将其值放入该局部变量中),将该局部变量传递为ref/ out,然后将该属性设置为该局部变量具有的值。但是,随后调用的方法本身并不会访问该属性,而是会访问您在此处创建的局部变量。
杰普·斯蒂格·尼尔森

5
@theberserker是的,尽管您可以在C#6中这样做public int Foo { get; },但是会创建带有只读后备字段的自动属性。
Michael Stum

83

忽略API问题,我发现使用属性最有价值的就是调试。

CLR调试器不支持数据断点(大多数本机调试器都支持)。因此,不可能在读取或写入类的特定字段时设置断点。在某些调试方案中,这是非常有限的。

由于属性是作为非常薄的方法实现的,因此可以在读取和写入其值时设置断点。这使他们在田野上有很大的优势。


2
十年后,这里至少是.NET Core的数据断点在这里:)
Luaan

72

从字段更改为属性会破坏合同(例如,要求重新编译所有引用代码)。因此,当您与其他班级(任何公共(且受一般保护)的成员)建立交流点时,您需要计划未来的发展。通过始终使用属性来执行此操作。

如今,将其设置为自动属性并没有什么,下线3个月后,您就意识到要对其进行延迟加载,并在getter中放入空值检查。如果您使用过一个字段,那么这最好是一次重新编译更改,而在最坏的情况下则是不可能重新编译的更改,具体取决于谁以及您的程序集还依赖什么。


9
我喜欢这个答案,因为它不使用单词“反射”,“界面”或“替代”。(对“合同”实在太糟糕)

65

只是因为没有人提到它:您无法在Interfaces上定义字段。因此,如果必须实现定义属性的特定接口,则自动属性有时是一个非常不错的功能。


1
我要说的是,如果您需要一个定义属性的接口,那么它应该是一个抽象类。仅仅因为c#允许您在接口中定义属性,并不意味着您应该使用它们。这是糟糕的设计。
奥德斯

8
@odyodyodys-我不确定我是否认为这是错误的设计。请解释您的理由?
Zaid Masud 2012年

4
@odyodyodys我同意zooone9243:Imp,从设计的角度来看,声明属性和声明getter / setter对(接口的常见做法)之间没有区别。
MartinStettner

22
@ zooone9243,+ MartinStettner:那是6个月前,从那时起我学到了很多东西。我正在收回它:)
奥德斯(Odys)2012年

47

巨大的差异通常被忽略,并且在其他任何答案中都没有提及:覆盖。您可以声明属性为虚拟并覆盖它们,而对于公共成员字段则不能这样做。


10

这全部与版本控制和API稳定性有关。在版本1中没有什么区别-但是稍后,如果您决定需要在版本2中将此属性设置为带有某种类型的错误检查的属性,则无需更改API-除了在任何地方,都无需更改代码属性的定义。


10

与公共字段相比,自动实现的属性的另一个优点是,您可以将集合访问器设为私有或受保护的,从而提供比公共字段更好地控制对象的类。


8

做田野没错public。但是请记住,getter/setter使用private字段创建不是封装。IMO,如果您不关心a的其他功能Property,也可以这样做public


1

如果您稍后决定通过与集合或数据库进行比较来检查标题是否唯一,则可以在属性中执行此操作,而无需更改依赖该标题的任何代码。

如果仅使用公共属性,则灵活性会降低。

对于我来说,使用属性对我来说最重要的是不违反合同,这是最重要的,在我真正需要灵活性之前,自动生成才是最有意义的。


0

我发现非常有用的一件事以及所有代码和测试原因是,如果它是属性而不是字段,则Visual Studio IDE会显示属性的引用,而不是字段的引用。


0

我的观点之后做了一些研究

  1. 验证。
  2. 允许覆盖访问器以更改属性的行为。
  3. 调试目的。通过在访问器中设置断点,我们将能够知道属性何时更改以及更改了什么。
  4. 我们可以只设置一个字段。例如,public set()和private get()。在公共场所这是不可能的。

它确实为我们提供了更多的可能性和可扩展性。

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.