19

可能重复:
约定问题:何时使用 Getter/Setter 函数而不是使用属性

最近我在 Getters 和 Setters 上遇到了很多不同的意见,所以我想我应该把它变成它自己的问题。

我之前的一个问题收到了立即评论(后来被删除),指出 setter 不应该有任何副作用,并且SetProperty方法将是更好的选择。

事实上,这似乎也是微软的观点。但是,它们的属性通常会引发事件,例如设置Resized表单WidthHeight属性时。OwenP还声明“你不应该让属性抛出异常,属性不应该有副作用,顺序不重要,并且属性应该相对快速地返回。”

然而Michael Stum指出,在 setter 中验证数据时应该抛出异常。如果您的设置器没有抛出异常,您如何有效地验证数据,正如这个问题的许多答案所暗示的那样?

当您需要像几乎所有 Microsoft 的 Control 一样发起事件时怎么办?那么,您是否不受订阅您活动的人的摆布?如果他们的处理程序执行大量信息,或者自己抛出错误,你的 setter 会发生什么?

最后,getter 中的延迟加载怎么样?这也可能违反以前的指导方针。

什么可以放在 getter 或 setter 中,什么应该只保留在访问器方法中?

编辑:

来自MSDN 中的另一篇文章:

和方法一般与其他方法没有区别getset它们可以执行任何程序逻辑、抛出异常、被覆盖以及使用编程语言允许的任何修饰符进行声明。但是请注意,属性也可以是静态的。get如果一个属性是静态的,那么和set方法可以做什么是有限制的。有关详细信息,请参阅您的编程语言参考。

4

3 回答 3

15

我的观点:

  1. 如果预计 setter 或 getter 会很昂贵,请不要将其设为属性,而应将其设为方法。

  2. 如果设置属性由于更改而触发事件,这很好。您还如何允许听众收到更改通知?但是,您可能希望提供 BeginInit/EndInit 对来抑制事件,直到进行所有更改。通常,事件处理程序有责任迅速返回,但如果您真的不相信它会这样做,那么您可能希望在另一个线程中发出事件信号。

  3. 如果设置属性在无效值上引发异常,那也没关系。这是在值完全错误时发出问题信号的合理方式。在其他情况下,您设置一堆属性,然后调用使用它们做某事的方法,例如建立连接。这将允许在使用属性之前推迟验证和错误处理,因此属性不需要抛出任何东西。

  4. 访问属性可能会产生副作用,只要它们不是意外且无关紧要。这意味着 getter 中的 JIT 实例化很好。同样,每当进行更改时为实例设置脏标志就可以了,因为它设置了相关属性,例如相同值的不同格式。

  5. 如果它做某事而不是仅仅访问一个值,它应该是一个方法。方法是动词,因此创建连接将由 OpenConnection() 方法完成,而不是 Connection 属性。Connection 属性将用于检索正在使用的连接,或将实例绑定到另一个连接。

编辑 - 添加 5,更改 2 和 3

于 2010-05-25T20:55:57.383 回答
1

我同意 getter/settings 不应该有副作用的想法,但我会说它们不应该有不明显的副作用。

至于抛出异常,如果您将属性设置为无效值(在非常基本的意义上),那么验证异常就可以了。但是,如果 setter 正在运行一系列复杂的业务规则验证,或者试图关闭并更新其他对象,或者任何其他可能导致异常的事情,那就不好了。但是这个问题并不是异常本身的问题,而是设置器正在关闭并秘密执行调用者不会(或不应该)期望的许多功能。

事件也是如此。如果一个 setter 抛出一个事件说“这个属性改变了”,那么没关系,因为这是一个明显的副作用。但是,如果它触发了其他一些自定义事件,从而导致一些隐藏的代码在系统的另一部分执行,那就很糟糕了。

这与我避免在 getter 中延迟加载的原因相同。事实上,它们在很多时候可以让事情变得更容易,但在某些时候它们会让事情变得更加混乱,因为在你想要加载子对象的时候总是会出现复杂的逻辑。当您填充父对象时,通常只需要多行代码来显式加载子对象,并且可以避免对对象状态的很多混淆。但是这方面可能会变得非常主观,而且很大程度上取决于情况。

于 2010-05-25T20:57:34.783 回答
0

无论如何,在使用 C# 工作时,我总是发现保守的方法是最好的。因为属性在语法上与字段相同,所以它们应该像字段一样工作:没有异常,没有验证,没有有趣的事情。(事实上​​,我的大部分属性都是从简单的字段开始的,直到绝对必要时才成为属性。)这个想法是,如果你看到看起来像是在获取或设置字段集的东西,那么它就像是在获取或设置字段集。设置一个字段,根据功能(没有抛出异常)、整体效率(例如,设置变量不会触发级联的委托调用)和对程序状态的影响(设置一个变量会设置该变量,并且不会t 召集许多可以做任何事情的代表)。

属性集要做的明智的事情包括设置一个标志来指示发生了变化:

set {
    if(this.value!=value) {
        this.changed=true;
        this.value=value;
    }
}

也许实际上在另一个对象上设置了一个值,例如:

set { this.otherObject.value=value; }

也许可以稍微解开输入,以简化类的内部代码:

set {
    this.isValid=(value&Flags.IsValid)!=0;
    this.setting=value&Flags.SettingMask;
}

(当然,在后两种情况下,get 函数可能正好相反。)

如果需要发生更复杂的事情,特别是调用委托、执行验证或抛出异常,那么我的观点是函数更好。(很多时候,我的字段通过 get 和 set 变成属性,然后最终成为 get 属性和 set 函数。)对于 getter 也是如此;如果您要返回对某物的引用,那没问题,但是如果您要创建一个全新的大对象并在每次读取该属性时填充它- 不是那么热。

于 2010-05-25T21:05:49.600 回答