属性的重点是什么?


11

以下是一些有关属性的参数和我的反参数:

比编写getter和setter方法更容易使用

Getter和setter方法对是代码的味道。编写起来更容易,就像使用Scantron表格并填写所有“ C”来简化数学测试失败一样。对于持久性,仅包含状态的对象不应使用getter / setter,而在持久性时应创建不可变的对象。

对于对象的消费者而言,重要的是它的作用,而不是它的作用方式。它的行为就是它所做的;它的状态是如何执行的。如果您发现自己关心对象的状态(持久性除外,尽管这也会破坏OO),那么您根本就没有在做OOP而失去了它的优势。

它们粗略地向消费者表明了性能

对于任何给定的财产,这可能会在将来发生变化。假设在版本1.0中,访问PropertyX只是返回一个字段。在版本1.5中,如果该字段为null,则PropertyX使用Null Object模式创建一个新的null对象。在2.0版中,该字段正在通过PropertyX中的getter方法进行进一步验证。

随着属性变得越来越复杂,使用属性的性能指标似乎越来越不真实。

他们比公共领域更好

这是真的。但是方法也是如此。

它们代表的是与方法根本不同的对象方面,并且对象的所有使用者都应注意这一点

您确定以上两个陈述都是正确的吗?

他们更容易打字,伙计

当然,打字myObject.Length比打字更容易myObject.Length(),但是不能用一点句法糖来解决吗?


为什么使用方法而不是属性?

  • 没有性能保证。即使方法变得更复杂,API也会保持真实。如果消费者遇到性能问题,并且他们不依赖API的话,他们将需要分析其代码。

  • 较少供消费者考虑。该酒店有塞特犬吗?一种方法肯定不会。

  • 消费者从正确的OOP思维方式进行思考。作为API的使用者,我有兴趣与对象的行为进行交互。当我在API中看到属性时,它看起来很像状态。实际上,如果属性做得太多,它们甚至都不应该是属性,因此,实际上,API对用户来说是处于状态的属性。

  • API的程序员将更深入地考虑具有返回值的方法,并尽可能避免在此类方法中修改对象的状态。应当尽可能将命令与查询分开。


所以我问你,为什么要使用属性而不是方法?MSDN上的大多数要点本身就是代码的味道,它们既不属于属性也不属于方法。

(这些想法是在考虑了CQS之后想到的。)


5
+1。属性是具有字段语法的方法,只是没有意义。
Nemanja Trifunovic

23
@Nemanja Trifunovic:如果写了type GetFoo() void SetFoo()一万遍,那是很有意义的。在编写C#代码的所有时间里,我从未对属性感到困惑。
Ed S.

23
在我看来,您已经下定决心了。问题是什么?
迪恩·哈丁

9
@Nemanja Trifunovic:字母二传手通常都很糟糕”,您可以根据需要重复多次,但这不会成为现实。如何解释为什么您认为它们不好?至于由于键入属性名称而不是基础变量名称而导致SO崩溃,这是不可能错过的错误,因为一旦调用该属性,它将使您的程序崩溃,这种情况很少见。当它确实发生时,我确切地知道了造成它的原因,并在两秒钟内修复了它。我没看到问题。
Ed S.

3
只是路过指出,以@NemanjaTrifunovic这是为什么getter和setter方法是邪恶的文章是相当,是关于java的,并强调一个java的弱点:疲软不具有的属性。(因此炫耀了太多的实现细节)…在这里咬我们的尾巴。(它的标题应该是getters和setters在Java中是不好的,因为我们对这些没有很好的想法
ZJR

Answers:


44

为什么是“针对属性的方法”?一种方法可以执行某些操作。属性是对象的成员。它们是完全不同的东西,尽管通常编写的两种方法(getter和setter)对应于属性。由于将属性与方法进行比较通常没有意义,因此我假设您要谈论的是getter / setter方法。

Getter和setter方法对是代码的味道。

我认为不一定,但是无论哪种方式,这都不是getters / setter与属性的问题,而是是否应使用其中的一个问题。还要注意,您可以例如省略setX零件,就像可以设置只读属性一样。

对于对象的消费者而言,重要的是它的作用,而不是它的作用方式。它的行为就是它所做的;它的状态是如何执行的。如果您发现自己关心对象的状态(持久性除外,尽管这也会破坏OO),那么您根本就没有在做OOP而失去了它的优势。

高度可疑的态度。如果GUI要输出a持有的数据DataStuffManagerImpl,则需要以某种方式从中获取该数字(不行,将应用程序的一半塞入窗口小部件类中是不可行的)。

随着属性变得越来越复杂,使用属性的性能指标似乎越来越不真实。

[方法有]没有性能保证。即使方法变得更复杂,API也会保持真实。如果消费者遇到性能问题,并且他们不依赖API的话,他们将需要分析其代码。

在几乎所有情况下,所有验证逻辑等仍然有效地为O(1),或者在成本上可以忽略不计。如果没有,也许您走得太远了,该改变了。getX()通常,一种方法也被视为便宜!

当然,输入myObject.Length比输入myObject.Length()更容易,但是用一点语法糖就能解决吗?

属性就是那个语法糖。

较少供消费者考虑。该酒店有塞特犬吗?一种方法肯定不会。

“有这个吸气器的安装工吗?” 一样的区别。

API的程序员将更深入地考虑具有返回值的方法,并尽可能避免在此类方法中修改对象的状态。应当尽可能将命令与查询分开。

我不确定您要在这里告诉我们什么。对于财产,有一个更强大的不成文法则不会引起副作用。


3
+1,这不是成文法。MSDN上的财产使用指南与其他许多人一起编写。我认为OP应该仔细检查它们,以消除许多疑问。而且,由于我是通过手机编写的,因此无法在此处发布链接,但是指南很容易找到。
飓风2011年


@delnan:属性不是那种语法糖。可以像对待字段一样对待属性(如果它们具有设置器,它们可以用作分配目标)。方法不能。
xofz 2011年

1
@SamPearson:obj.x = y映射至obj.setX(y)obj.x映射至obj.getX()。有什么不同?

1
@SamPearson,您可以彼此独立地更改属性设置器和获取器的可见性。我一直将其与Entity Framework对象一起使用:我的设置器受到保护(因此只有框架可以设置值)。所以说int length = myObject.Length而不允许myObject.Length = 10
Michael Brown

19

Getter和setter方法对是代码的味道。

那是一个错误的假设,而且完全不正确。Get / Set方法是一种封装数据成员的方法,绝不是“代码异味”。我真的很讨厌有人扔掉“代码气味”一词,但是无论如何...

对于任何给定的财产,这可能会在将来发生变化。假设在版本1.0中,访问PropertyX只是返回一个字段。在版本1.5中,如果该字段为null,则PropertyX使用Null Object模式创建一个新的null对象。在2.0版中,该字段正在通过PropertyX中的getter方法进行进一步验证。

随着属性变得越来越复杂,使用属性的性能指标似乎越来越不真实。

听起来像API设计不良,我看不出这是属性语法的错误。

C#中的属性只是一种语法糖,对于一种非常常见的模式而言,这使我们作为程序员的生活更加轻松。但是,这些天来,如果您想使用数据绑定,则必须“使用”属性,因此它们现在已经比语法糖多了一点。我想我真的完全不同意您的任何观点,而且我不明白您为什么不喜欢直接支持通用模式的语言功能。


1
我将提供一个比代码气味更好的术语。只需阅读一本有关重构的书,其中使用了大约一千次。就像黑板上的指甲。
JeffO 2011年

1
@Jeff O:是的,我发现似乎最经常使用该术语的人经验不足。
Ed S.

6
@Jeff O和@Ed S:IME,“代码气味”已成为人们真正的意思是“我不喜欢它,但实际上没有理由”时使用的术语
Steven Evers

3
@SnOrfus:是的,或者当他们持有在实践中通常没有多大意义的“宗教”观点时,例如“一种方法的行数绝不能超过X行”,这通常只是他们在某处阅读的内容的重复。
Ed S.

1
不恰当的使用getter / setter方法是一个代码气味,但因此也应该是使用不当的东西。即使当事物具有狭窄的用例时,将这些事物用于那些用例也不应被视为代码异味。为所有事物创建二传手不应该是一个盲目的习惯,但应该不怕在适当的时候将它们包括在内。必需的是平衡能够更改状态对客户的用处,以及将来能够找出状态更早的状态(如果状态是不可变的,则很容易)。两者都是有用的,但是代码必须选择一个。
supercat 2014年

10

你的前提是错的。

属性绝不是性能,内部实施或其他方面的契约。
属性是特殊的方法对,如果可以的话,它们在语义上表示属性。因此,唯一的约定是,属性的​​行为与您期望的属性的行为相同。
如果我要求对象的颜色,则不做任何假设,即它是通过纯字段访问获得的,还是是按时计算的。
关键是要能够通过使用方法来公开属性的行为,而对于接口的使用者来说却是透明的,无论您使用的是字段还是访问器。

具有属性(并且实际上隐藏属性的实现,因为属性与字段相对)是一种强大的声明性功能,这是具有可视对象检查器和其他自动视图生成,数据绑定以及许多其他有用功能的基础。最重要的是,它也清楚地为您的其他程序员传输了这些信息。


6

消费者从正确的OOP思维方式进行思考。作为API的使用者,我有兴趣与对象的行为进行交互。当我在API中看到属性时,它看起来很像状态。实际上,如果属性做得太多,它们甚至都不应该是属性,因此,实际上,API对用户来说是处于状态的属性。

属性应该是状态。如果基础的setter或getter代码进行了更改,从而大大增加了设置或获取状态所需的处理,那么实际上它不再是属性,您应该替换为方法。作为代码开发人员,这取决于您。

在setter或getter中放入太多(多少是多少取决于代码)是错误的,但是仅仅因为有可能并不是在所有情况下都放弃该模式的原因。


6

您缺少的一件事,就是属性ARE方法,我不知道这是由于无知还是您有意忽略了此细节。

当您使用C#的属性语法时,编译器会悄悄地创建带有元数据的getter和setter方法,该方法将告诉理解属性的语言作为一流的语法功能来对待它们。实际上,这就是您要的语法糖。可以在CLR上运行的几种语言不会完全掩盖您的细节(嘘,如果我愿意的话,还有一些Lisp实现),并且您可以显式调用getter和setter。

属性有时会产生副作用,例如触发更改通知事件(例如INotifyPropertyChanged)或将类的实例标记为脏。这是它们的真正价值所在,尽管创建它们的工作要多一些(Boo中除外,它具有精通语法的宏)。

现在,更广泛的问题是,getter和setter方法实际上是否是一件好事。显然存在权衡;如果您有mutator方法,则代码固有地并行性较差,因为现在您必须处理线程同步问题。而且,很可能您会使用增变器方法(无论是属性还是getter / setter方法;它们是等效的)违反Demeter定律,因为这样做很方便。

但是有时候这是正确的做法。有时,您的问题是从某个来源获取数据,对其进行一些更改,将更新的数据呈现给用户,然后保存更改。该习语由属性很好地服务。正如艾伦·霍鲁布(Allen Holub)的文章所述,仅仅因为吸气剂和吸气剂存在问题并不意味着您永远都不要使用它们。(模仿抽象的Guruspeak认为是有害的)。即使您遵循“班级/职责/协作者”方法,您仍然最终会向某人公开信息或信息展示;重点是最大程度地减少纠缠的表面积。

属性的好处是让另一个对象利用它们很容易。属性的缺点是让另一个对象利用它们很容易,而您不能轻易避免这种情况。

在例如MVC架构中,对象突变在控制器层有意义。那是你的合作者。您的视图也是一个协作者,但它不应直接更改您的对象,因为它的职责不同。将表示代码放到业务对象中也不一定是避免获取/设置程序的正确方法。

如果您使用功能而非抽象的OO,则方程式会稍有变化,这通常会使带有几个更改的“记录”对象的副本变得微不足道,尽管并非免费。嘿,如果您想获得性能保证,则通常比懒惰地重建不可变的集合更容易预测属性。


5

我的书中使用属性的另一个重要原因是,它们可以大大提高可读性。

account.setBalance(Account.getBalance()+deposit);

显然,其可读性不如

account.Balance += deposit;

6
除了不应该将Balance作为属性,因为肯定会有与之相关的重要业务逻辑(检查透支,收取服务费,记录交易等)。如果将其应用于其他示例,则可读性点是完全有效的(温度,BackgroundColour等)
Kate Gregory

9
为什么不使用account.Deposit(amount)?
xofz 2011年

4
@Sam Pearson:+1。一个很好的例子说明了为什么吸气剂/定阻剂经常表示设计不佳。
Nemanja Trifunovic

4

你对我几乎有相反的宗教观点:)

当我从Delphi迁移到Java时,缺少属性对我来说是一种极大的侮辱。我习惯于声明一个属性,然后直接将其指向私有变量,或者编写一个(受保护的)getter和setter方法,然后将它们“连接”到该属性。这封装了属性,并控制了如何以明确的明确方式访问它。您可能拥有一个只读属性,该属性会在访问时计算总数,并将其称为“总计”,而不是“ _getTotal()”或类似的圆珠。这也意味着您可以通过使属性在抽象基类中受保护,然后将其移到公共或在子类中发布来限制对属性的访问。

对我来说,属性是设置和获取对象状态的一种更自然,更具表现力的方式。依赖于非强制性的编码约定,而不是您要批评其属性的任何东西,更像是一种“代码味道”。而且,正如其他人所说的那样,提出对属性的错误设计使用并利用其失败来尝试并驳斥属性的概念通常是极其糟糕的形式。您可能只看到过对属性的不良使用,并假设这就是我所想的方式,但是您应该在变得过于教条之前进行更多研究。


+1确实是对属性的真正使用。好答案。“ 这还意味着您可以通过使属性在抽象基类中受到保护,然后将其移到公共或子类中来限制对属性的访问。
Karthik Sreenivasan 2012年

德尔福滥用财产和恶心。您不对列表进行排序,而是将其sorted 属性设置为true。因此,将其设置false为原始列表即可。:D还是随机洗牌?:D或其他,这只是愚蠢的。此外,与适当的排序集相比,它真慢。属性还不错,但是我学会了非常谨慎地使用它们(直到对getter和setter感到非常满意为止;我主要使用Java)。
maaartinus

1

在Java Beans Framework中引入属性的原因之一是允许与IDE集成-您可以将这些组件拖动到IDE并使用属性编辑器编辑属性。

(那时它们只是常规的获取器和设置器-并且仅由命名约定定义-并且确实确实闻到了)

如今,甚至有更多情况下,例如在调试时,不通过常规代码访问和修改属性。


1

另一个角度-他们确实来自VB。请记住,在20世纪的黑暗时期,.NET的一个主要重点是它是VB的一种简单的直接替代品,因此您的VB程序员可以将其拖放到Webforms上。VB具有属性,因此您需要保留该功能,以便正确翻译。


1
Anders Heljsberg设计了C#(以及某些IL),还设计了具有特性的Delphi。
Jesse C. Slicer

1

属性至少有两个用途:

  • 即使在实现方式上不同,它们在概念上与字段相同。吸气剂不应改变对象的状态,也不应有副作用。设置员仅应在概念上类似于修改字段的方式来更改状态,而不会产生其他副作用。

  • 它们在语法上与公共(可能是只读)字段相同。这意味着可以在不中断源级别调用者的情况下将公共字段更改为属性。


是的,但请注意“应该”一词。关于属性的任何事情都不能阻止吸气剂产生副作用,实际上我已经看到很多情况了。
Nemanja Trifunovic

1
将公共字段更改为属性破坏二进制兼容性,必须重新编译消耗代码-尽管未修改,我想这是您得到的内容:)
MattDavey 2011年

@MattDavey:谢谢。这就是我的意思。编辑。
dsimcha's
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.