属性 vs 公共成员变量

17
可能是重复问题: 什么是C#中的字段和属性的区别 我是一名初级程序员,已经了解了类属性。书籍说明属性允许您间接访问成员变量。好的,那么它与直接将字段公开并直接访问有何不同?
以下是Jesse Liberty在《学习C# 3.0》中的一句话:
例如,您可能希望外部类能够读取值,但不能更改该值;或者您可能希望编写一些代码,以便内部字段只能接受特定范围内的值。如果您授予外部类对成员字段的自由访问权,则无法控制任何内容。
我不理解他在这里说什么。有人能进一步解释一下或举例说明为什么我要使用属性而不是将字段公开吗?就我现在的理解,它们都可以完成完全相同的事情...所以我显然在这里遗漏了什么。

请查看此网站: http://msdn.microsoft.com/zh-cn/library/79b3xss3(v=vs.80).aspx - MethodMan
正确的链接是http://msdn.microsoft.com/en-us/library/79b3xss3.aspx。除非您只想让链接与特定版本相关,否则请不要发布特定版本的链接。 - John Saunders
6个回答

11
到目前为止,提供的其他答案详细介绍了访问器/修改器逻辑的优点,但似乎都忽略了关于对象封装的意识形态问题。
你看,类成员字段是实现细节。例如,如果您有一个表示集合的类,那么您可以将其实现为链表(并通过公共字段公开根节点)或者您可以将其实现为可调整大小的数组并公开索引0成员。
揭示实现细节的问题在于,您失去了类和其使用者之间任何定义接口的方式。通过确保所有操作都通过定义的方法完成(由类本身控制),使其更易于处理并提供长期视角。例如,您可以更轻松地将集合实现从一种类型(链表)转换为另一种类型(数组),而不会违反与类的使用者的任何约定。
不要担心微不足道的访问器/修改器方法对性能的影响:JIT编译器将内联属性方法。如果运行一些基准测试,您将看到属性与字段的性能相同。

2
这里是最佳答案。值得一提的是,如果以后需要从公开字段更改为属性,则这也代表着合同变更,并且需要重新编译所有相关代码... 如果您分发库,则尤其糟糕。 - spender

10
他的意思是属性可以提供一个getter但不能提供setter,因此使它们成为只读(例如)。
属性只是方法的语法糖,例如。
public int SomeProperty { get; set; }

是一个简化版的写法,等同于

private int _someProperty;

public int SomeProperty_get() 
{
   return _someProperty;
}

public void SomeProperty_set(int value) 
{
   _someProperty = value;
}
这意味着属性的设置器和获取器可以应用逻辑,而仅有公共字段则不行。
编辑:我不确定CLR为自动属性提供的后备字段的字段名是什么 - 这只是一个示例 :)
编辑2:
只读属性的示例:
public int SomeProperty { get; }

最后是针对自动属性的公共读-私有写

public int SomeProperty { get; private set; }

当你不想手动输入后备字段时,它非常有用 :)

请记住,如果可能需要对成员应用逻辑,则属性是正确的选择。这是许多框架的工作方式(例如,通过使用属性来告诉某种对象管理器已更改某些内容以跟踪“脏”对象,这将无法使用公共字段实现)。


9

属性可能具有副作用,它们提供了对“getter”和“setter”方法的语法糖。

public class MyClass {

   int sizeValue = 0;

   public int Size {
      get {
         return sizeValue;
      }
      set {
         if ( value < 10 ) throw new Exception("Size too small");
         sizeValue = value;
      }
   }
}

属性在 get 和 set 方面也可以有不同的保护级别,而字段则没有这种能力。

public class MyOtherClass {

   // only this object can set this.
   public int Level {
      get; private set; 
   }

   // only things in the same assembly can set this.
   public string Name {
      get; internal set;
   }
}

我不同意"语法糖"这个贬义词。我认为"属性(properties)"远非如此简单。在我看来... - paulsm4
6
我不同意你对于“语法糖”一词的解释,认为它是带有贬义的。它被称作“糖”是因为它是令人向往的。 :) - Dan J
@paulsm4,这并不是负面的断言。除了很好地包装get/set方法逻辑之外,您还可以获得反射的不同类型,这可能会有所帮助。 - IanNorton
你错过了主要的要点(封装、稳定接口)。 - H H

3

“属性”和“成员访问”之间有许多重要的区别。

最重要的是,通过属性,您可以使成员只读(可以访问状态,但无法更改它)。就像Java中的“getter()”和“setter()”方法一样。

您还可以从属性返回计算值(生成一个“即时”值,就像它是一个变量)。


另一个重要的功能是能够在setter访问器中添加数据验证。 - Sam Axe
我认为你可能混淆了术语:你可以使一个属性只读。但你不能使一个成员变量只读。 - Dan J
1
@DanJ:当然可以;public readonly string Foo; - Ed S.
@Ed S:你说得对 ;) - paulsm4
另外需要注意的是,“属性”在很大程度上受到Anders Hejlsberg早期使用Delphi进行工作的启发(他是Borland Pascal,Delphi和C#的架构师,还有许多其他成就)。 - paulsm4
显示剩余2条评论

1

属性可以进行配置,以便:

它们是只读的,Public MyProp {get;}

它们是只写的 Public MyProp {set;}

它们可以被外部对象读取,但只能由类的内部设置

Public MyProp {get; private set;}

正如其他人所发表的,您还可以在getter和setter中放置逻辑。例如,在允许将属性设置为新值之前,您可以检查该值是否可接受。

您无法使用公共字段执行任何操作。

基本上,公共字段是您可以拥有的最愚蠢的属性。鉴于.Net现在允许为您的属性自动备份字段,没有使用公共字段的好理由了。


自动实现属性很好用,但是当前的编译器在使用它们时不会生成关于字段误用的警告(例如“字段从未被使用”或“字段从未被赋值,将始终具有默认值null”)。这使得捕获这些错误稍微困难一些,因为它们会被忽略。然而,我在精神上同意你的看法。 - Dai

0
如果你有一个 Public Int MyAge,我可以将它设置为-200或20,000,而你无能为力。
如果你使用属性,你可以检查年龄是否在0到150之间,例如。
编辑:根据IanNorton的示例(天啊,他真快)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接