为什么更喜欢使用属性而不是公共变量?

19

除了在setter中对值进行健全性检查之外,是否存在更基本的原因来优先选择属性而不是公共变量?


有效地复制自https://dev59.com/PHRB5IYBdhLWcg3wXmVI and https://dev59.com/HnRC5IYBdhLWcg3wMeHf#379057 -- 还有其他的。 - tvanfosson
8个回答

30

我们之前提到过这个主题,但现在我找不到任何相关内容。

简而言之:你的需求可能会发生变化:如果现在没有进行合理性检查,在将来可能需要进行检查。然而,如果您将公共字段更改为属性,则会破坏二进制兼容性:每个使用您的代码/库的客户端都必须重新编译。

这是很糟糕的,因为这可能会花费大量的金钱。

从一开始就使用属性可以避免这个问题。这甚至适用于不是库的代码。为什么?因为你永远不知道:即使是高度特定于领域的代码!也可能证明有用,所以你想将其重构为一个库。如果您已经使用属性代替公共/受保护的字段,则此重构过程显然变得更加容易。

此外,在C#3.0中编写公共属性很容易,因为您可以使用自动实现的属性,节省了相当多的代码:

public DataType MyProperty { get; set; }

我会替你实现必要的后备字段和getter/setter代码。

我想补充一句:.NET在这方面的行为有点懒惰。编译器可以动态将公共字段更改为属性,从而避免这个问题。VB6已经对公开的COM类执行了此操作,我认为VB.NET和C#没有理由不这样做。也许编译器团队中的某个人(Jared?)可以对此发表评论。


3
忽略“应该”这个问题,编译器无法做到这一点有许多原因。其中两个立即想到的原因是:1)名称和2)访问属性不同于访问字段(性能、引用和输出)。不幸的是,这里没有足够的空间来真正评论。 - JaredPar
4
用户要求一个物品,但你提供的是不同的替代品,导致该帖子被关闭。简而言之,用户会因此感到不满意。 - JaredPar
1
我也曾这样想,但结构体总是会引起问题:v.S.M = 5 将 5 赋值给结构体 S 的成员 M。如果 S 是一个字段,那么就没有问题。但如果它是一个属性,这只会修改从属性 getter 返回的 S 的临时副本中的 M。可变结构体是许多问题的根源! - Daniel Earwicker
如果您将公共字段更改为属性,则会破坏二进制兼容性:使用您的代码/库的每个客户端都必须重新编译...这经常被引用,但在我看来似乎是错误的。您能否想到一种情况,其中第三方向您提供了某个程序集的新版本,而您由于方法/类/命名空间的更改而无需重新编译?通常,您将希望重新编译以使用vNew中的某些内容。 - Orion Edwards
@OrionEdwards 说实话,更多的人应该编写库。事实上,通过编写良好的代码,大部分代码应该驻留在可重用的库中,而相对较少的代码应该驻留在前端应用程序中。我承认我也有随意编写代码的罪过,但这并不代表它是良好的软件工程。如果您严格使用库,稳定的接口将成为巨大的福利。 - Konrad Rudolph
显示剩余2条评论

12

简言之:

  • 您可以控制访问权限(只读,
    只写,读/写)
  • 在设置属性时,您可以验证值(检查是否为空等)
  • 您可以进行其他处理,例如延迟初始化
  • 您可以更改底层实现。例如,属性现在可能由成员变量支持,但您可以将其更改为由数据库行支持,而不会破坏任何用户代码。

C#中的readonly关键字怎么样? - Aaron Franke

5

Jeff Atwood在他的博客中谈到了这个问题:

There are valid reasons to make a trivial property, exactly as depicted above:

  • Reflection works differently on variables vs. properties, so if you rely on reflection, it's easier to use all properties.
  • You can't databind against a variable.
  • Changing a variable to a property is a breaking change.

It's a shame there's so much meaningless friction between variables and properties; most of the time they do the exact same thing. Kevin Dente proposed a bit of new syntax that would give us the best of both worlds:

public property int Name;

However, if the distinction between variable and property is such an ongoing problem, I wonder if a more radical solution is in order. Couldn't we ditch variables entirely in favor of properties? Don't properties do exactly the same thing as variables, but with better granular control over visibility?


1
那个博客链接已经不能正常访问了。 - Aaron Franke

3
将来将字段更改为属性被视为“破坏性更改”。字段被认为是类的实现细节,公开它们会破坏封装性。

只有在调用代码无法重新编译时,才会造成突破性变化,大多数情况下您拥有调用者代码,并且它由相同的构建系统编译。因此,这对于.NET框架团队以外的大部分人来说并不是问题。 - Ian Ringrose
@Ian Ringrose:它也不是完全的源代码兼容。例如,您的代码可能会执行ref obj.MyField。如果您不更改源代码,则无法将MyField更改为MyProperty - Mehrdad Afshari
@Ian:这是一个重大变更- http://msdn.microsoft.com/zh-cn/library/ms182141(VS.80).aspx - Cerebrus

2

使用属性可以使您的代码更加面向对象。通过将成员变量公开,您正在暴露您的实现细节。

还可以参考C#编程指南中的link


1

你也可以通过属性来保护写入访问并允许读取访问:

public int Version { get; private set; }

1
这段代码实际上无法编译。我认为你的意思是:public int Version { get; private set; } - Ian R. O'Brien

1
如果您在封闭的环境中工作——您不开发SDK,所有类都在同一个项目框架中使用——那么没有区别。
通常的论点是“将来您可能需要对值进行一些检查,因此使用属性更容易”。我完全不同意这个观点。
使用公共字段更易读,装饰更少,使用更简单。

0

是的。

考虑一个现在持有字符串的公共变量,你可以简单地设置它。然而,如果你决定这个公共变量应该持有一个对象,并且这个对象应该用一个字符串初始化,那么你就必须改变所有使用原始对象的代码。但如果你使用setter,你只需要更改setter以用提供的字符串初始化对象。


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