属性 vs. 字段:需要帮助理解使用属性而不是字段的原因

42

首先,我已经阅读了关于此主题的一系列帖子,但因为我理解封装和字段修饰符(private、public等)的含义,所以我不认为我掌握了属性的概念。

C#的一个主要方面就是通过封装来保护代码中的数据。我“认为”我理解了使用修饰符(private、public、internal、protected)的原因在于它们能够实现这种封装。然而,学习了属性后,我有些困惑,不仅是对属性的用途,还有封装(即我理解的数据保护)在C#中的整体重要性/能力。

更具体地说,当涉及到C#中的属性时,所有我所了解的内容都表明应该尽可能使用属性代替字段,因为:

1) 它们允许您在不能进行直接访问字段时更改数据类型。

2) 它们添加了一层数据访问保护。

然而,根据我之前了解的字段修饰符的作用,似乎它们也能实现 #2,那么对我来说,属性只是额外生成的代码,除非您有某些原因需要更改类型(#1),因为您正在(或多或少地)创建访问字段的隐藏方法而不是直接访问。

另外,修饰符也可以添加到属性中,这使我更加困惑了需要使用属性来访问数据的原因。

我从不同作家的“属性”章节中读了很多内容,但没有一个能真正解释清楚属性与字段、封装(以及良好的编程方法)之间的关系。

有人能够解释:

1)为什么我要使用属性而不是字段(特别是当它似乎只是添加了额外的代码)

2)如何识别属性并将其视为非常量方法(除了get;set;明显的情况),当跟踪其他人的代码时?

3)任何关于何时使用何种技术的一般准则吗?

感谢您阅读这篇长文并对此表示歉意- 我不想只是问一个已经被问了100次的问题而不解释为什么我会再次提出它。


1
重复的问题?https://dev59.com/pk7Sa4cB1Zd3GeqP0Ahg - Fredrik Mörk
2
似乎还没有人链接到为什么属性很重要。过去我发现这个概述非常有帮助。 - Roman
12个回答

22

1) 为什么我应该使用属性而不是字段(特别是当我只是添加额外的代码时)

在可能的情况下,你应该始终使用属性。它们抽象了直接访问字段(如果你没有创建字段,则会为你创建)。即使属性仅仅是设置一个值,它也可以在以后保护你。将字段更改为属性后,这是一项破坏性的更改,因此如果您有一个公共字段并想将其更改为公共属性,则必须重新编译最初访问该字段的所有代码。

2) 有没有什么提示可以识别属性的用途,而不是将其视为仅仅是方法(除了get;set显而易见),当跟踪其他人的代码时?

我不太确定你在问什么,但在跟踪其他人的代码时,你应该总是假设属性正在执行除了获取和设置值之外的操作。尽管通常不建议在getter和setter中放置大量的代码,但你不能仅仅因为它是属性就认为它会快速执行。

3) 关于何时使用什么的良好编程方法,是否有任何基本法则?

我总是尽可能使用属性的获取和设置方法。这样,如果我需要检查值是否在某些范围内,是否为null等,我可以稍后添加代码。如果不使用属性,则必须返回并在每个直接访问字段的地方放置这些检查。


  1. 这正是我不同意1)的原因。属性会使调用现场的代码阅读变得困难。
- Jo So
1
在这里提出这个额外的问题,因为它得到了最多的赞。那么私有成员呢?你仍然更喜欢属性还是字段? - gabe
经过思考,实际上我通常不使用属性来访问私有成员。这可能是因为我的编程风格。我创建的内部变量通常是只读的。 - kemiller2002

11

Properties的一个好处是getter和setter可以具有不同的访问级别。考虑以下代码:

public class MyClass {

  public string MyString { get; private set; }

  //...other code
}

这个属性只能在内部更改,例如在构造函数中。阅读一下关于依赖注入的知识。构造函数注入和属性注入都处理从某种外部配置中设置属性。现在有很多框架可供使用。如果您深入研究其中一些,您将对属性及其用法有所了解。依赖注入还将帮助您解答第三个问题中有关良好实践的问题。

当查看其他人的代码时,您可以通过它们的图标区分方法和属性。此外,在 IntelliSense 中,属性摘要的第一部分是单词“Property”。


10

不必担心通过属性访问字段所需的额外代码,它将被JIT编译器“优化”(通过内联代码)。除非它太大而无法内联,但那时你仍然需要额外的代码。

此外,定义简单属性所需的额外代码也很少:

public int MyProp { get; set; } // use auto generated field.

当你需要自定义时,你可以随时定义自己的字段。

因此,你的代码会多一层封装/数据保护,这是很好的。

我的规则: 始终通过属性公开字段


9

虽然我绝对不喜欢直接向公众暴露字段,但还有另一件事情:字段不能通过接口暴露,而属性可以。


8
  1. There are several reasons why you might want to use Properties over Fields, here are just a couple:

    a. By having the following

    public string MyProperty { get; private set; }
    

    you are making the property "read only". No one using your code can modify it's value. There are cases where this isn't strictly true (if your property is a list), but these are known and have solutions.

    b. If you decide you need to increase the safety of your code use properties:

    public string MyProperty
    {
        get { return _myField; }
        set
        {
            if (!string.IsNullOrEmpty(value))
            {
                _myField = value;
            }
        }
    }
    
  2. You can tell they're properties because they don't have (). The compiler will tell you if you try to add brackets.

  3. It's considered good practise to always use properties.


1
@Josh - readonly 表示该值无法在任何地方更改,即使在同一类中也不行。使用具有私有 setter 的属性意味着您可以在类内修改该值,但是您的类的用户不能这样做。 - ChrisF

3

使用字段通常在私有类中使用,这些类不打算与其他类共享数据。当我们希望数据可以被其他类访问时,我们使用属性。属性通过getset方法实现数据在私有类和其他类之间的共享,这些方法称为自动属性。您还可以在同一类中使用访问修饰符Full Property,将属性与私有字段链接起来,以便该类既可以将数据作为私有字段使用,又可以将其作为属性使数据可被其他类访问。看下面这个简单的例子:

private string _name;  
public string Name    
{
   get
     {
       return _name;
     }
   set
     {
      _name = value;
     }
}

私有字符串 _name 仅可被类内部使用,而 Name 属性可以被同一命名空间中的其他类访问。


3

有许多情况下使用简单字段不会造成损害,但是
属性更容易在以后进行更改,例如,如果您想在值更改时添加事件或者想执行一些值/范围检查。

此外,如果您有几个相互依赖的项目,则必须重新编译所有依赖于更改字段为属性的项目。


2
为什么我想要使用属性而不是字段(尤其是当看起来我只是添加了额外的代码)?
您想要使用属性而不是字段,因为当您使用属性时,可以将事件与它们一起使用。因此,在某些情况下,当属性更改时,您可以绑定一些处理程序到PropertyChanging或PropertyChanged事件。在字段的情况下,这是不可能的。字段可以是公共的、私有的或受保护的,而在属性的情况下,您可以使它们在公开时只读,但在私有时可写。
如何识别属性的使用,以免将其视为简单的方法(除了get;set明显之外),当跟踪其他人的代码时?
当每次调用时期望返回值是动态的时候,应该使用方法;当返回值不是那么动态时,应该使用属性。
在关于何时使用何种方法方面,是否有任何通用的经验法则?
是的,我强烈建议阅读Framework Design guidelines以获取最佳编程实践。

1

属性是实现封装的首选方式。然而,它们在功能上是多样化的,您可以公开不同类型的属性并进行强制转换;您可以更改访问修饰符;它们用于WinForms数据绑定;它们允许您嵌入轻量级的每个属性逻辑,例如更改通知等。

查看其他人的代码时,属性具有不同的智能感知图标,与方法不同。

如果您认为属性只是额外的代码,我会建议您仍然坚持使用它们,但通过从字段自动生成属性来使您的生活更轻松(右键单击 -> 重构 -> 封装字段...)


1

属性允许您在使用它们时执行除设置或获取值之外的其他操作。最重要的是,它们允许您执行验证逻辑。

最佳实践是将任何公开的内容都作为属性暴露出来。这样,如果您稍后更改了设置/获取逻辑,您只需要重新编译类,而不是与其链接的每个类。


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