题 为什么要使用字段而不是属性?


我对C#很新,我觉得属性很棒。事实上,如此精彩,我看不到使用字段的任何真正优势。即使对于私人领域,似乎属性提供的灵活性和模块性最多可以避免严重的麻烦,最糟糕的是根本没有效果。

我可以在字段中看到的唯一优势是您可以内联初始化它们。但是大多数时候,你想在构造函数中初始化它们。如果您不使用内联初始化,是否有任何理由不使用属性?

编辑:有些人提出需要使用字段备份属性(显式或自动)。让我澄清一下我的问题:是否有任何理由使用字段 除了备份属性?也就是说,有没有时间 SomeType someField; 比较好 SomeType SomeProperty { get; set; }

编辑2:DanM,Skurmedel和Seth都提供了非常有用的答案。我已经接受了DanM,因为它是最完整的,但如果有人将他们的回答总结为一个答案,我会很乐意接受它。


31
2018-01-30 01:28


起源


重复的 stackoverflow.com/questions/295104/... - user73993
所以让我们关闭它 - Rubens Farias
不,我认为这是一个非常优秀的问题,确实也非常微妙。绝对不同。 - Mark Byers
这不是重复的。另一个问题是“A和B之间有什么区别”;这一个承认了A和B之间的区别,并问“A渲染B是否过时?” - apollodude217
在C#6.0中,自动属性将支持内联初始化和 readonly 关键字,这使得这个问题更加贴切。 - hypehuman


答案:


通常,属性需要一个支持字段,除非它们是简单的getter / setter“自动属性”。

所以,如果你只是在......

public string Name { get; set; } // automatic property

......你不需要一个领域,我同意,没有理由拥有一个领域。

但是,如果你正在......

public string Name
{
    get { return _name; }
    set 
    {
       if (value = _name) return;
       _name = value;
       OnPropertyChange("Name");
    }
}

......你需要那个 _name 支持领域。

对于不需要任何特殊get / set逻辑的私有变量,它实际上是一个判断调用,无论是私有自动属性还是只是一个字段。我通常做一个领域,然后,如果我需要它 protected 要么 public,我会把它改成自动财产。

更新

正如Yassir所指出的那样,如果你使用自动属性,那么幕后仍然会有一个潜伏的场地,这不是你实际需要输入的东西。因此,底线是:属性不存储数据,它们提供对数据的访问。字段是实际保存数据的内容。所以,即使你看不到它们,你也需要它们。

更新2

关于你修改过的问题......

有没有时间SomeType someField;比较好 SomeType SomeProperty { get; set; }

......有一件事情浮现在脑海中:如果你有一个私人领域,并且(根据私人领域的惯例),你可以称之为私人领域 _name,这向您和任何阅读您的代码的人发出信号,表明您正在使用私人数据。另一方面,如果你把所有东西都作为财产,并且(按照财产惯例),你可以打电话给你的私有财产 Name,现在你不能只看变量并告诉它是私有数据。因此,仅使用属性会删除一些信息。我没有尝试过与所有房产合作来衡量这是否是关键信息,但肯定会丢失一些东西。

另一件事,更轻微的是 public string Name { get; set; } 需要更多的打字(并且有点麻烦) private string _name


22
2018-01-30 01:32



您应该提到编译器为每个auto属性创建一个字段 - Hannoun Yassir
我认为海报的重点是:即使您提供的示例中的_name字段也可能是私有的自动实现属性。从理论上讲,您根本不需要了解这些字段,它们只是属性如何工作的实现细节,编译器可以完全隐藏它们作为实现细节,就像CPU寄存器一样。 - Mark Byers
@Mark,你在技术上是正确的,但使用私有属性作为“后备属性”似乎违反了惯例,这会使你的代码的意图不清楚,需要阅读或维护它的人。它还会产生命名问题。如果是公共财产 Name你会称之为私有财产?如果你打电话 _name 要么 _Name,其他人会阅读你的代码可能会形成关于该变量是属性还是字段的错误结论。也就是说,也许有一天,属性和字段之间的区别将消失。 - devuxer
@DanM:谢谢你的回答。然而,我真正得到的是它们之间的区别 SomeType someField; 和 SomeType SomeProperty { get; set; }。在幕后, SomeProperty 可能有自动实现的字段,但从程序员的角度来看,有什么理由可以选择 someField?我编辑了我的原始问题以澄清这一点。 - Matthew
@Dan:“那就是说,也许有一天,属性和领域之间的区别将会消失。”我也这么认为。但它可能需要几代人。我们最近刚刚将自己断开了<s>汇编程序</ s> C ++。 - Mark Byers


属性是一件很棒的事情 - 但是存在与属性访问相关的开销。不一定是问题,但需要注意的事项。

避免过度使用财产吸气剂和二传手 

大多数人都没有意识到,在开销方面,属性getter和setter类似于方法;它主要是区分它们的语法。除了字段访问之外不包含任何指令的非虚拟属性getter或setter将由编译器内联,但在许多其他情况下,这是不可能的。你应该仔细考虑你对财产的使用;从类内部直接访问字段(如果可能的话),并且从不盲目地重复调用属性而不将值存储在变量中。总而言之,这并不意味着你应该使用公共领域!

资源: http://dotnet.sys-con.com/node/46342


11
2018-01-30 01:34



“非虚拟属性getter或setter不包含除字段访问之外的其他指令将由编译器内联”他所指的情况(私有自动实现的属性)将属于此类别,因此不会影响性能这个。 - Mark Byers


在使用ref / out args时尝试使用Property:

someObject.SomeMethod(ref otherObject.SomeProperty);

它不会编译。


10
2018-04-01 13:18





如果你想要的东西 readonly 你几乎必须使用一个字段,因为没有办法告诉自动属性生成一个只读字段。

我经常这样做。

举例:

class Rectangle
{
   private readonly int _width;
   private readonly int _height;

   public Rectangle(int width, int height)
   {
      _width = width;
      _height = height;
   }

   public int Width { get { return _width; } }
   public int Height { get { return _height; } }
}

这意味着Rectangle内部的任何内容都不能改变构造后的宽度或高度。如果一个人试图编译器会抱怨。

如果我使用私有setter的自动属性,编译器将不会保护我自己。

我看到的另一个原因是,如果不需要公开一些数据(留下来 private)为什么要把它作为财产?


9
2018-01-30 01:42



这很好知道。你为什么不能这样做 public readonly int Width { get; set; }? - Matthew
不知道。我只能猜测;也许它被削减为支持其他功能,或者他们没有看到需要。你不得不问Eric Lippert那个:) - Skurmedel
因为你只能在构造函数中调用readonly字段(或者在声明它们时),我无法想象用于声明readonly属性的好语法是什么样子。 - Skurmedel
@Matthew:“为什么你不能公开readonly int Width {get; set;}”我认为这是另一个很好的问题。 :)我也想知道答案。 - Mark Byers
C#6.0现在支持只读属性! object MyProp { get; } 此属性可以内联设置(object MyProp { get; } = ...)或在构造函数中,但没有其他地方(就像只读字段)。 - Matthew


我不明白你为什么要使用私人autoproperties。那有什么好处

private int Count {get; set;}

过度

private int count

4
2018-01-30 01:48



默认属性可以是虚拟的,因此可以覆盖它,并且您可以独立控制get和set访问(例如,使get访问器公共,并设置访问器受保护)。如果您最初不需要这些,但稍后可能需要它们怎么办?然后,您可以从字段开始并稍后使用属性,除非您通过反射访问字段或属性。所以...优点是有时只有。 - apollodude217
值得注意的是,如果您稍后因为需求更改而从字段转换为属性,则必须重新编译引用该字段的任何内容。 - Brandon Barkley


虽然我同意我认为David Basarab的声明中的“意图”:“没有理由公开揭露字段,”我想补充一点略有不同:

我将以上大卫的引用修改为:“除了通过有意识地选择封装属性中的字段(通过严格控制访问权限)之外,没有理由公开暴露字段......在类之外......

在“C#”字段上,属性不仅仅是语法的“贴面”:它们是一个基本的语言特性,其设计理由很充分,包括:

  1. 控制暴露的内容而不暴露在类外(封装,数据隐藏)

  2. 允许在访问或设置属性时执行某些操作:最好在属性“获取和”设置中表示的操作,而不是“提升”到外部定义的方法。

  3. 设计接口不能定义'字段:但可以定义属性。

良好的OO设计意味着有意识地选择“状态”:

  1. 局部变量字段:什么状态是方法和私有 短暂的 :局部变量通常仅在方法体的范围内有效,或者甚至在“for循环”范围内具有“缩短的寿命”。当然,您也可以将方法中的参数变量视为“本地”。

  2. 类实例字段:什么状态是类的私有状态,并且对于类的每个实例都具有独立存在,但很可能需要在类的多个位置使用。

  3. 静态实例字段:什么状态只是类的属性,与类的实例数无关。

  4. 故意并有意识地暴露出类“外部”:关键的想法是,类和本类所暴露的数据的“消费者”之间至少存在一个间接层。当然,“曝光”的“反面”是隐藏(封装,隔离)实现代码的意图。

    一个。通过公共财产:这里所有其他答案都涵盖了这方面的所有方面

    湾通过索引器

    C。通过方法

    d。公共静态变量通常位于实用程序类中,通常是静态类。

建议你点评: 关于'Fields的MSDN ... 关于属性的MSDN ... 关于索引器的MSDN


4
2018-01-30 02:22





没有 公开揭露领域的原因。

如果您公开公开某个字段,则无法更改信息来源,从内联定义到配置文件而不进行重构。

您可以使用字段隐藏内部数据。我很少赞成这一点,当我做一些公开隐藏并在房产中使用时,我只使用字段。 (即我没有使用自动属性生成)


3
2018-01-30 01:32



不仅如此,在隐藏信息的具体类别中,公开揭露这些领域是违反OOP范式的...... - t0mm13b
我不认为这真的回答了他的问题。他想知道为什么在某些情况下推荐使用字段而不是属性。 - Mark Byers
没有理由公开字段的声明是不正确的;如果是这样,该功能将是非法的。公开揭露一个领域有很多充分的理由。例如,构建一个可变结构,以便在具有旧COM对象的互操作方案中使用。当对象的字段作为传统COM对象方法的“ref”或“out”参数传递时,这尤其密切相关。 - Eric Lippert
我在处理使用“ref”或“out”时使用字段。设置转移到本质上是DTO的代码需要大量额外的代码。所以我只是将属性更改为字段。为什么让事情复杂化? - Nathan


字段是您可以存储州的唯一地方。属性实际上只是一对具有特殊语法的方法,允许它们根据它们的使用方式映射到get或set方法:如果属性修改或访问状态,该状态仍然必须存储在字段中。

你并不总是看到这些字段。使用C#3自动属性,编译器会为您创建该字段。但它仍然存在。此外,自动属性具有一些重要的限制(例如,没有INotifyPropertyChanged支持,在setter中没有业务逻辑),这意味着它们通常是不合适的,并且您无论如何都需要创建显式字段和手动定义的属性。

按照 大卫的回答,如果你在谈论API,那你就是对的:你几乎从不想让内部状态(字段)成为API的一部分。


3
2018-01-30 01:35